mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-06 22:03:59 -05:00
feat(frontend): add templates/triggers to new Library page view (#11580)
## Changes 🏗️ Add Templates to the new Agent Library page: <img width="800" height="889" alt="Screenshot 2025-12-09 at 14 10 01" src="https://github.com/user-attachments/assets/85f0d478-f3f9-4ccf-81df-b9a7f4ae8849" /> - You can create a template from a run ( new action button ) - Templates are listed and can be selected on the sidebar - When viewing a template, you can edit it, create a task or delete it Add Triggers to the new Agent Library page: <img width="800" height="836" alt="Screenshot 2025-12-09 at 14 10 43" src="https://github.com/user-attachments/assets/c722f807-c72f-4a4d-8778-e36bea203f6e" /> - When an agent contains a trigger block, on the modal it will create a trigger - When there are triggers, they are listed on the sidebar - A trigger can be viewed and edited ## 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: - [x] Run the new page locally
This commit is contained in:
@@ -10,10 +10,13 @@ import { AgentRunsLoading } from "./components/other/AgentRunsLoading";
|
||||
import { EmptySchedules } from "./components/other/EmptySchedules";
|
||||
import { EmptyTasks } from "./components/other/EmptyTasks";
|
||||
import { EmptyTemplates } from "./components/other/EmptyTemplates";
|
||||
import { EmptyTriggers } from "./components/other/EmptyTriggers";
|
||||
import { SectionWrap } from "./components/other/SectionWrap";
|
||||
import { LoadingSelectedContent } from "./components/selected-views/LoadingSelectedContent";
|
||||
import { SelectedRunView } from "./components/selected-views/SelectedRunView/SelectedRunView";
|
||||
import { SelectedScheduleView } from "./components/selected-views/SelectedScheduleView/SelectedScheduleView";
|
||||
import { SelectedTemplateView } from "./components/selected-views/SelectedTemplateView/SelectedTemplateView";
|
||||
import { SelectedTriggerView } from "./components/selected-views/SelectedTriggerView/SelectedTriggerView";
|
||||
import { SelectedViewLayout } from "./components/selected-views/SelectedViewLayout";
|
||||
import { SidebarRunsList } from "./components/sidebar/SidebarRunsList/SidebarRunsList";
|
||||
import { AGENT_LIBRARY_SECTION_PADDING_X } from "./helpers";
|
||||
@@ -109,6 +112,21 @@ export function NewAgentLibraryView() {
|
||||
scheduleId={activeItem}
|
||||
onClearSelectedRun={handleClearSelectedRun}
|
||||
/>
|
||||
) : activeTab === "templates" ? (
|
||||
<SelectedTemplateView
|
||||
agent={agent}
|
||||
templateId={activeItem}
|
||||
onClearSelectedRun={handleClearSelectedRun}
|
||||
onRunCreated={(execution) => handleSelectRun(execution.id, "runs")}
|
||||
onSwitchToRunsTab={() => setActiveTab("runs")}
|
||||
/>
|
||||
) : activeTab === "triggers" ? (
|
||||
<SelectedTriggerView
|
||||
agent={agent}
|
||||
triggerId={activeItem}
|
||||
onClearSelectedRun={handleClearSelectedRun}
|
||||
onSwitchToRunsTab={() => setActiveTab("runs")}
|
||||
/>
|
||||
) : (
|
||||
<SelectedRunView
|
||||
agent={agent}
|
||||
@@ -127,6 +145,10 @@ export function NewAgentLibraryView() {
|
||||
<SelectedViewLayout agentName={agent.name} agentId={agent.id}>
|
||||
<EmptyTemplates />
|
||||
</SelectedViewLayout>
|
||||
) : activeTab === "triggers" ? (
|
||||
<SelectedViewLayout agentName={agent.name} agentId={agent.id}>
|
||||
<EmptyTriggers />
|
||||
</SelectedViewLayout>
|
||||
) : (
|
||||
<SelectedViewLayout agentName={agent.name} agentId={agent.id}>
|
||||
<EmptyTasks agent={agent} />
|
||||
|
||||
@@ -48,8 +48,8 @@ export function CredentialsSelect({
|
||||
onValueChange={(value) => onSelectCredential(value)}
|
||||
>
|
||||
<SelectTrigger className="h-auto min-h-12 w-full rounded-medium border-zinc-200 p-0 pr-4 shadow-none">
|
||||
<SelectValue asChild>
|
||||
{selectedCredentials ? (
|
||||
{selectedCredentials ? (
|
||||
<SelectValue key={selectedCredentials.id} asChild>
|
||||
<CredentialRow
|
||||
credential={{
|
||||
id: selectedCredentials.id,
|
||||
@@ -64,10 +64,10 @@ export function CredentialsSelect({
|
||||
readOnly={readOnly}
|
||||
asSelectTrigger={true}
|
||||
/>
|
||||
) : (
|
||||
<Text variant="large">Select credential</Text>
|
||||
)}
|
||||
</SelectValue>
|
||||
</SelectValue>
|
||||
) : (
|
||||
<SelectValue key="placeholder" placeholder="Select credential" />
|
||||
)}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{credentials.map((credential) => (
|
||||
|
||||
@@ -75,11 +75,11 @@ export function getActionButtonText(
|
||||
hasExistingCredentials: boolean,
|
||||
): string {
|
||||
if (hasExistingCredentials) {
|
||||
if (supportsOAuth2) return "Connect a different account";
|
||||
if (supportsApiKey) return "Use a different API key";
|
||||
if (supportsUserPassword) return "Use a different username and password";
|
||||
if (supportsHostScoped) return "Use different headers";
|
||||
return "Add credentials";
|
||||
if (supportsOAuth2) return "Connect another account";
|
||||
if (supportsApiKey) return "Use a new API key";
|
||||
if (supportsUserPassword) return "Add a new username and password";
|
||||
if (supportsHostScoped) return "Add new headers";
|
||||
return "Add new credentials";
|
||||
} else {
|
||||
if (supportsOAuth2) return "Add account";
|
||||
if (supportsApiKey) return "Add API key";
|
||||
|
||||
@@ -4,6 +4,12 @@ import { GraphExecutionJobInfo } from "@/app/api/__generated__/models/graphExecu
|
||||
import { GraphExecutionMeta } from "@/app/api/__generated__/models/graphExecutionMeta";
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { Button } from "@/components/atoms/Button/Button";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/atoms/Tooltip/BaseTooltip";
|
||||
import { Dialog } from "@/components/molecules/Dialog/Dialog";
|
||||
import { useState } from "react";
|
||||
import { ScheduleAgentModal } from "../ScheduleAgentModal/ScheduleAgentModal";
|
||||
@@ -147,15 +153,45 @@ export function RunAgentModal({
|
||||
|
||||
<Dialog.Footer className="mt-6 bg-white pt-4">
|
||||
<div className="flex items-center justify-end gap-3">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={handleOpenScheduleModal}
|
||||
disabled={
|
||||
isExecuting || isSettingUpTrigger || !allRequiredInputsAreSet
|
||||
}
|
||||
>
|
||||
Schedule Task
|
||||
</Button>
|
||||
{!allRequiredInputsAreSet ? (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={handleOpenScheduleModal}
|
||||
disabled={
|
||||
isExecuting ||
|
||||
isSettingUpTrigger ||
|
||||
!allRequiredInputsAreSet
|
||||
}
|
||||
>
|
||||
Schedule Task
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>
|
||||
Please set up all required inputs and credentials before
|
||||
scheduling
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
) : (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={handleOpenScheduleModal}
|
||||
disabled={
|
||||
isExecuting ||
|
||||
isSettingUpTrigger ||
|
||||
!allRequiredInputsAreSet
|
||||
}
|
||||
>
|
||||
Schedule Task
|
||||
</Button>
|
||||
)}
|
||||
<RunActions
|
||||
defaultRunType={defaultRunType}
|
||||
onRun={handleRun}
|
||||
|
||||
@@ -69,8 +69,8 @@ export function ModalRunSection() {
|
||||
|
||||
{inputFields.length > 0 ? (
|
||||
<ModalSection
|
||||
title="Task Setup"
|
||||
subtitle="Enter the information needed for the agent to run this task"
|
||||
title="Task Inputs"
|
||||
subtitle="Enter the information you want to provide to the agent for this task"
|
||||
>
|
||||
{/* Regular inputs */}
|
||||
{inputFields.map(([key, inputSubSchema]) => (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export function WebhookTriggerBanner() {
|
||||
return (
|
||||
<div className="rounded-lg border border-blue-200 bg-blue-50 p-4">
|
||||
<div className="mb-4 rounded-lg border border-blue-200 bg-blue-50 p-4">
|
||||
<div className="flex items-start">
|
||||
<div className="flex-shrink-0">
|
||||
<svg
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { useGetV1GetUserTimezone } from "@/app/api/__generated__/endpoints/auth/auth";
|
||||
import {
|
||||
getGetV1ListGraphExecutionsInfiniteQueryOptions,
|
||||
usePostV1ExecuteGraphAgent,
|
||||
} from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import { usePostV2SetupTrigger } from "@/app/api/__generated__/endpoints/presets/presets";
|
||||
import {
|
||||
getGetV1ListExecutionSchedulesForAGraphQueryKey,
|
||||
usePostV1CreateExecutionSchedule as useCreateSchedule,
|
||||
} from "@/app/api/__generated__/endpoints/schedules/schedules";
|
||||
getGetV2ListPresetsQueryKey,
|
||||
usePostV2SetupTrigger,
|
||||
} 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";
|
||||
@@ -38,25 +36,12 @@ export function useAgentRunModal(
|
||||
const { toast } = useToast();
|
||||
const queryClient = useQueryClient();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [showScheduleView, setShowScheduleView] = useState(false);
|
||||
const [inputValues, setInputValues] = useState<Record<string, any>>({});
|
||||
const [inputCredentials, setInputCredentials] = useState<Record<string, any>>(
|
||||
{},
|
||||
);
|
||||
const [presetName, setPresetName] = useState<string>("");
|
||||
const [presetDescription, setPresetDescription] = useState<string>("");
|
||||
const defaultScheduleName = useMemo(() => `Run ${agent.name}`, [agent.name]);
|
||||
const [scheduleName, setScheduleName] = useState(defaultScheduleName);
|
||||
const [cronExpression, setCronExpression] = useState(
|
||||
agent.recommended_schedule_cron || "0 9 * * 1",
|
||||
);
|
||||
|
||||
// Get user timezone for scheduling
|
||||
const { data: userTimezone } = useGetV1GetUserTimezone({
|
||||
query: {
|
||||
select: (res) => (res.status === 200 ? res.data.timezone : undefined),
|
||||
},
|
||||
});
|
||||
|
||||
// Determine the default run type based on agent capabilities
|
||||
const defaultRunType: RunVariant = agent.has_external_trigger
|
||||
@@ -94,38 +79,6 @@ export function useAgentRunModal(
|
||||
},
|
||||
});
|
||||
|
||||
const createScheduleMutation = useCreateSchedule({
|
||||
mutation: {
|
||||
onSuccess: (response) => {
|
||||
if (response.status === 200) {
|
||||
toast({
|
||||
title: "Schedule created",
|
||||
});
|
||||
callbacks?.onCreateSchedule?.(response.data);
|
||||
// Invalidate schedules list for this graph
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getGetV1ListExecutionSchedulesForAGraphQueryKey(
|
||||
agent.graph_id,
|
||||
),
|
||||
});
|
||||
analytics.sendDatafastEvent("schedule_agent", {
|
||||
name: agent.name,
|
||||
id: agent.graph_id,
|
||||
cronExpression: cronExpression,
|
||||
});
|
||||
setIsOpen(false);
|
||||
}
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast({
|
||||
title: "❌ Failed to create schedule",
|
||||
description: error.message || "An unexpected error occurred.",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const setupTriggerMutation = usePostV2SetupTrigger({
|
||||
mutation: {
|
||||
onSuccess: (response: any) => {
|
||||
@@ -134,6 +87,11 @@ export function useAgentRunModal(
|
||||
title: "Trigger setup complete",
|
||||
});
|
||||
callbacks?.onSetupTrigger?.(response.data);
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getGetV2ListPresetsQueryKey({
|
||||
graph_id: agent.graph_id,
|
||||
}),
|
||||
});
|
||||
setIsOpen(false);
|
||||
}
|
||||
},
|
||||
@@ -220,33 +178,25 @@ export function useAgentRunModal(
|
||||
[allRequiredInputsAreSetRaw, credentialsRequired, allCredentialsAreSet],
|
||||
);
|
||||
|
||||
const notifyMissingRequirements = useCallback(
|
||||
(needScheduleName: boolean = false) => {
|
||||
const allMissingFields = (
|
||||
needScheduleName && !scheduleName ? ["schedule_name"] : []
|
||||
)
|
||||
.concat(missingInputs)
|
||||
.concat(
|
||||
credentialsRequired && !allCredentialsAreSet
|
||||
? missingCredentials.map((k) => `credentials:${k}`)
|
||||
: [],
|
||||
);
|
||||
const notifyMissingRequirements = useCallback(() => {
|
||||
const allMissingFields = missingInputs.concat(
|
||||
credentialsRequired && !allCredentialsAreSet
|
||||
? missingCredentials.map((k) => `credentials:${k}`)
|
||||
: [],
|
||||
);
|
||||
|
||||
toast({
|
||||
title: "⚠️ Missing required inputs",
|
||||
description: `Please provide: ${allMissingFields.map((k) => `"${k}"`).join(", ")}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
[
|
||||
missingInputs,
|
||||
scheduleName,
|
||||
toast,
|
||||
credentialsRequired,
|
||||
allCredentialsAreSet,
|
||||
missingCredentials,
|
||||
],
|
||||
);
|
||||
toast({
|
||||
title: "⚠️ Missing required inputs",
|
||||
description: `Please provide: ${allMissingFields.map((k) => `"${k}"`).join(", ")}`,
|
||||
variant: "destructive",
|
||||
});
|
||||
}, [
|
||||
missingInputs,
|
||||
toast,
|
||||
credentialsRequired,
|
||||
allCredentialsAreSet,
|
||||
missingCredentials,
|
||||
]);
|
||||
|
||||
// Action handlers
|
||||
const handleRun = useCallback(() => {
|
||||
@@ -257,7 +207,7 @@ export function useAgentRunModal(
|
||||
|
||||
if (defaultRunType === "automatic-trigger") {
|
||||
// Setup trigger
|
||||
if (!scheduleName.trim()) {
|
||||
if (!presetName.trim()) {
|
||||
toast({
|
||||
title: "⚠️ Trigger name required",
|
||||
description: "Please provide a name for your trigger.",
|
||||
@@ -268,7 +218,7 @@ export function useAgentRunModal(
|
||||
|
||||
setupTriggerMutation.mutate({
|
||||
data: {
|
||||
name: presetName || scheduleName,
|
||||
name: presetName,
|
||||
description: presetDescription || `Trigger for ${agent.name}`,
|
||||
graph_id: agent.graph_id,
|
||||
graph_version: agent.graph_version,
|
||||
@@ -291,7 +241,6 @@ export function useAgentRunModal(
|
||||
}, [
|
||||
allRequiredInputsAreSet,
|
||||
defaultRunType,
|
||||
scheduleName,
|
||||
inputValues,
|
||||
inputCredentials,
|
||||
agent,
|
||||
@@ -303,70 +252,6 @@ export function useAgentRunModal(
|
||||
toast,
|
||||
]);
|
||||
|
||||
const handleSchedule = useCallback(() => {
|
||||
if (!allRequiredInputsAreSet) {
|
||||
notifyMissingRequirements(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!scheduleName.trim()) {
|
||||
toast({
|
||||
title: "⚠️ Schedule name required",
|
||||
description: "Please provide a name for your schedule.",
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
createScheduleMutation.mutate({
|
||||
graphId: agent.graph_id,
|
||||
data: {
|
||||
name: presetName || scheduleName,
|
||||
cron: cronExpression,
|
||||
inputs: inputValues,
|
||||
graph_version: agent.graph_version,
|
||||
credentials: inputCredentials,
|
||||
timezone:
|
||||
userTimezone && userTimezone !== "not-set" ? userTimezone : undefined,
|
||||
},
|
||||
});
|
||||
}, [
|
||||
allRequiredInputsAreSet,
|
||||
scheduleName,
|
||||
cronExpression,
|
||||
inputValues,
|
||||
inputCredentials,
|
||||
agent,
|
||||
notifyMissingRequirements,
|
||||
createScheduleMutation,
|
||||
toast,
|
||||
userTimezone,
|
||||
]);
|
||||
|
||||
function handleShowSchedule() {
|
||||
// Initialize with sensible defaults when entering schedule view
|
||||
setScheduleName((prev) => prev || defaultScheduleName);
|
||||
setCronExpression(
|
||||
(prev) => prev || agent.recommended_schedule_cron || "0 9 * * 1",
|
||||
);
|
||||
setShowScheduleView(true);
|
||||
}
|
||||
|
||||
function handleGoBack() {
|
||||
setShowScheduleView(false);
|
||||
// Reset schedule fields on exit
|
||||
setScheduleName(defaultScheduleName);
|
||||
setCronExpression(agent.recommended_schedule_cron || "0 9 * * 1");
|
||||
}
|
||||
|
||||
function handleSetScheduleName(name: string) {
|
||||
setScheduleName(name);
|
||||
}
|
||||
|
||||
function handleSetCronExpression(expression: string) {
|
||||
setCronExpression(expression);
|
||||
}
|
||||
|
||||
const hasInputFields = useMemo(() => {
|
||||
return Object.keys(agentInputFields).length > 0;
|
||||
}, [agentInputFields]);
|
||||
@@ -375,7 +260,6 @@ export function useAgentRunModal(
|
||||
// UI state
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
showScheduleView,
|
||||
|
||||
// Run mode
|
||||
defaultRunType,
|
||||
@@ -394,10 +278,6 @@ export function useAgentRunModal(
|
||||
setPresetName,
|
||||
setPresetDescription,
|
||||
|
||||
// Scheduling
|
||||
scheduleName,
|
||||
cronExpression,
|
||||
|
||||
// Validation/readiness
|
||||
allRequiredInputsAreSet,
|
||||
missingInputs,
|
||||
@@ -409,15 +289,9 @@ export function useAgentRunModal(
|
||||
|
||||
// Async states
|
||||
isExecuting: executeGraphMutation.isPending,
|
||||
isCreatingSchedule: createScheduleMutation.isPending,
|
||||
isSettingUpTrigger: setupTriggerMutation.isPending,
|
||||
|
||||
// Actions
|
||||
handleRun,
|
||||
handleSchedule,
|
||||
handleShowSchedule,
|
||||
handleGoBack,
|
||||
handleSetScheduleName,
|
||||
handleSetCronExpression,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
|
||||
export function EmptyTriggers() {
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center justify-center gap-20">
|
||||
<div>
|
||||
<svg
|
||||
width="342"
|
||||
height="211"
|
||||
viewBox="0 0 342 211"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M212.148 114.272C193.65 114.272 175.622 86.479 175.622 86.479L169.237 78.4039L164.636 72.6762L163.979 71.8311L136.843 37.5588L135.247 35.4931C134.683 34.8358 134.026 34.0847 133.369 33.2396C128.58 26.9485 122.383 15.8687 126.702 10.0471C131.585 3.38044 145.857 -1.3144 155.998 2.15978C166.139 5.63396 170.646 10.5166 170.646 10.5166C170.646 10.5166 184.918 12.3945 193.65 40.1879C202.383 67.9814 227.078 55.7748 236.467 80.9391C242.571 97.3711 230.552 114.366 212.054 114.366L212.148 114.272Z"
|
||||
fill="#FF9C0F"
|
||||
/>
|
||||
<path
|
||||
d="M212.148 114.554C193.744 114.554 175.622 86.8547 175.434 86.573L135.059 35.5871C134.496 34.9298 133.932 34.1787 133.181 33.2397C121.913 18.3101 125.294 11.4557 126.514 9.67163C131.397 3.09885 145.669 -1.97157 156.092 1.6904C165.481 4.88289 170.176 9.38994 170.74 10.0472C171.397 10.1411 174.683 10.8923 179.003 14.5543C183.322 18.2162 189.331 25.5402 193.838 40.0003C198.251 54.2726 207.172 57.9345 215.716 61.5026C223.791 64.8829 232.148 68.3571 236.749 80.6575C239.66 88.451 238.627 96.7139 234.026 103.381C229.143 110.423 221.162 114.46 212.148 114.46C212.148 114.46 212.148 114.46 212.054 114.46L212.148 114.554ZM148.392 1.12703C139.754 1.12703 130.646 5.16458 126.984 10.1411C123.134 15.3054 127.923 25.3524 133.65 32.958C134.308 33.897 134.965 34.6481 135.434 35.2115L137.031 37.2773L175.81 86.2913C175.998 86.573 193.932 113.897 211.96 113.991C211.96 113.991 211.96 113.991 212.054 113.991C220.88 113.991 228.768 110.047 233.557 103.193C238.064 96.7139 239.096 88.5449 236.28 81.0331C231.772 69.0144 223.885 65.728 215.622 62.1599C206.984 58.4979 197.97 54.7421 193.463 40.282C188.956 26.0097 183.04 18.7796 178.815 15.2115C174.308 11.3618 170.74 10.7984 170.646 10.7984C170.646 10.7984 170.552 10.7984 170.458 10.7984C170.458 10.7984 165.857 5.91576 155.904 2.53548C153.557 1.69041 150.927 1.31482 148.298 1.31482L148.392 1.12703Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M212.148 114.272C193.651 114.272 175.622 86.479 175.622 86.479H170.176L169.143 78.4039L168.862 76.4321L174.59 68.3569C184.261 79.5306 205.294 90.986 219.848 88.9203C234.402 86.7607 236.28 80.9391 236.28 80.9391C242.383 97.371 230.364 114.366 211.867 114.366L212.148 114.272Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M212.148 114.554C194.402 114.554 176.843 88.8264 175.528 86.7606H170.27C170.082 86.7606 169.988 86.6668 169.988 86.479L168.768 76.432C168.768 76.432 168.768 76.3381 168.768 76.2442L174.496 68.1691C174.496 68.1691 174.589 68.0752 174.683 68.0752C174.683 68.0752 174.871 68.0752 174.871 68.1691C184.73 79.5306 205.669 90.7043 219.942 88.5447C234.12 86.479 236.186 80.8451 236.186 80.7513C236.186 80.6574 236.373 80.5635 236.467 80.5635C236.467 80.5635 236.655 80.5635 236.655 80.6573C239.566 88.4508 238.533 96.7137 233.932 103.38C229.05 110.423 221.068 114.46 212.054 114.46L212.148 114.554ZM170.458 86.1973H175.622C175.716 86.1973 175.81 86.1973 175.904 86.2912C176.092 86.5728 194.026 113.897 212.054 113.991C220.881 113.991 228.768 110.047 233.557 103.193C237.97 96.8076 239.003 89.0142 236.467 81.5963C235.528 83.1926 231.96 87.4179 220.035 89.1081C205.669 91.2677 184.73 80.1879 174.683 68.7325L169.143 76.432L170.364 86.1973H170.458Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M131.021 51.3617C131.021 51.3617 129.519 63.5683 134.777 69.0143C140.411 74.8359 154.402 73.5213 154.402 73.5213L152.524 86.4791H175.528L174.683 68.2631L167.078 57.3711C174.308 52.864 171.866 44.6011 167.453 42.1598C166.327 41.5964 165.106 41.3147 163.885 41.5964C157.782 42.911 158.345 53.1457 158.345 53.1457H154.402L150.552 39.0612C145.012 36.6199 138.251 33.5213 135.247 24.8828C135.247 25.1645 133.932 28.4509 133.463 32.6762C132.054 44.3194 127.266 45.0706 126.796 47.8875C126.327 50.6105 131.115 51.3617 131.115 51.3617H131.021Z"
|
||||
fill="#F48282"
|
||||
/>
|
||||
<mask
|
||||
id="mask0_912_14740"
|
||||
style={{ maskType: "luminance" }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="126"
|
||||
y="24"
|
||||
width="50"
|
||||
height="63"
|
||||
>
|
||||
<path
|
||||
d="M131.021 51.3615C131.021 51.3615 129.519 63.5681 134.777 69.0141C140.411 74.8357 154.402 73.5212 154.402 73.5212L152.524 86.4789H175.528L174.683 68.3568L167.078 57.4648C174.308 52.9578 171.866 44.6949 167.453 42.2536C166.327 41.5963 165.106 41.4085 163.885 41.6902C157.782 43.0047 158.345 53.2395 158.345 53.2395H154.402L150.552 39.155C145.012 36.7137 138.251 33.6151 135.247 24.9766C135.247 25.2583 133.932 28.5446 133.463 32.77C132.054 44.4132 127.266 45.1644 126.796 47.9813C126.327 50.7043 131.115 51.4554 131.115 51.4554L131.021 51.3615Z"
|
||||
fill="white"
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_912_14740)">
|
||||
<path
|
||||
d="M154.402 53.2393C154.402 53.2393 160.975 56.3378 161.162 61.9716C161.35 70.3284 149.425 72.3942 142.007 72.6759C132.148 73.0515 145.763 87.6993 145.763 87.6993C145.763 87.6993 170.928 84.5068 171.491 84.5068C172.054 84.5068 183.979 83.0984 183.979 83.0984C183.979 83.0984 181.35 47.6993 181.162 47.4176C180.975 47.136 170.176 36.0562 170.176 36.0562L157.313 42.1594L154.402 53.1454"
|
||||
fill="#2973E8"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M175.622 86.7604H152.618C152.618 86.7604 152.43 86.7604 152.43 86.6665V86.4787L154.214 73.8966C151.96 74.0844 139.942 74.7416 134.683 69.2956C130.646 65.0703 130.552 56.8074 130.646 53.4271C130.646 52.582 130.646 52.0186 130.74 51.7369C129.988 51.5491 127.829 51.0796 126.89 49.7651C126.514 49.2017 126.327 48.6383 126.42 47.9811C126.608 47.0421 127.172 46.2909 128.017 45.3519C129.613 43.474 132.054 40.6571 133.087 33.1454C133.087 33.0515 133.087 32.8637 133.087 32.7698C133.369 30.2346 134.026 27.5116 134.871 25.0703C134.871 24.8825 135.059 24.7886 135.153 24.7886C135.247 24.7886 135.341 24.7886 135.434 24.9764C138.439 33.521 145.2 36.5257 150.552 38.967C150.552 38.967 150.74 39.0609 150.74 39.1548L154.496 52.9576H157.97C157.97 51.1736 157.97 42.6289 163.791 41.4083C165.012 41.1266 166.327 41.4083 167.641 42.0656C170.176 43.474 171.866 46.5726 171.96 49.6712C171.96 52.8637 170.458 55.6806 167.547 57.5585L174.965 68.1689C174.965 68.1689 174.965 68.2627 174.965 68.3566L175.904 86.5726V86.7604C175.904 86.7604 175.81 86.7604 175.716 86.7604H175.622ZM152.899 86.197H175.341L174.496 68.3566L166.984 57.5585V57.3707C166.984 57.3707 166.984 57.1829 167.078 57.1829C169.988 55.305 171.585 52.6759 171.491 49.5773C171.491 46.6665 169.801 43.7557 167.453 42.4412C166.327 41.8778 165.2 41.5961 164.073 41.8778C158.251 43.0984 158.721 53.0515 158.721 53.1454V53.3332C158.721 53.3332 158.627 53.3332 158.533 53.3332H154.589C154.496 53.3332 154.308 53.3332 154.308 53.1454L150.458 39.2487C145.106 36.8073 138.533 33.8966 135.341 25.7275C134.589 27.9811 134.12 30.4224 133.838 32.6759C133.838 32.7698 133.838 32.9576 133.838 33.0515C132.805 40.6571 130.364 43.5679 128.674 45.4459C127.923 46.2909 127.359 46.9482 127.172 47.7933C127.172 48.2628 127.172 48.7322 127.453 49.1078C128.486 50.4224 131.209 50.8919 131.209 50.8919C131.397 50.8919 131.491 51.0796 131.491 51.1735C131.491 51.1735 131.491 51.9247 131.397 53.1454C131.209 56.4318 131.397 64.6008 135.247 68.6383C140.693 74.2721 154.496 73.0515 154.683 73.0515H154.871V73.2393L152.993 85.9153L152.899 86.197Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M134.402 42.7228C134.402 42.7228 136.092 39.6242 140.129 40.1876C144.167 40.751 144.73 43.3801 144.73 43.3801C144.73 43.3801 142.946 46.5726 139.284 46.1031C135.622 45.6336 134.402 42.7228 134.402 42.7228Z"
|
||||
fill="#FFFFFE"
|
||||
/>
|
||||
<mask
|
||||
id="mask1_912_14740"
|
||||
style={{ maskType: "luminance" }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="134"
|
||||
y="40"
|
||||
width="11"
|
||||
height="7"
|
||||
>
|
||||
<path
|
||||
d="M134.402 42.7228C134.402 42.7228 136.092 39.6242 140.129 40.1876C144.167 40.751 144.73 43.3801 144.73 43.3801C144.73 43.3801 142.946 46.5726 139.284 46.1031C135.622 45.6336 134.402 42.7228 134.402 42.7228Z"
|
||||
fill="white"
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_912_14740)">
|
||||
<path
|
||||
d="M138.064 46.2909C139.516 46.2909 140.693 45.1138 140.693 43.6618C140.693 42.2098 139.516 41.0327 138.064 41.0327C136.612 41.0327 135.435 42.2098 135.435 43.6618C135.435 45.1138 136.612 46.2909 138.064 46.2909Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M139.19 60.1877C139.002 60.1877 138.908 60.0938 138.908 59.906C138.908 58.4037 139.378 57.2769 140.129 56.7136C140.88 56.1502 142.007 56.1502 143.322 56.7136C143.509 56.7136 143.509 56.9013 143.509 57.0891C143.509 57.2769 143.322 57.2769 143.134 57.2769C142.007 56.8074 141.068 56.8074 140.411 57.2769C139.754 57.7464 139.378 58.6854 139.378 59.9999C139.378 61.3145 139.284 60.2816 139.096 60.2816L139.19 60.1877Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M144.543 39.437C144.543 39.437 144.355 39.437 144.355 39.3431C143.604 38.3102 142.101 37.559 140.317 37.3712C138.345 37.1834 136.561 37.7468 135.435 38.7797C135.341 38.8736 135.153 38.8736 135.059 38.7797C134.965 38.6858 134.965 38.498 135.059 38.4041C136.28 37.1834 138.251 36.6201 140.411 36.8079C142.383 36.9957 143.979 37.8407 144.824 39.0614C144.824 39.1553 144.824 39.3431 144.824 39.437C144.824 39.437 144.73 39.437 144.636 39.437H144.543Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M140.223 40.0942C140.223 40.0942 142.101 40.1881 143.51 41.4088C144.918 42.6294 145.294 43.5684 145.763 43.7562L145.2 44.4135C145.2 44.4135 142.946 40.8454 140.223 40.1881V40.0942Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M135.246 25.2581C135.153 25.2581 135.059 25.2581 134.965 25.0703C134.683 24.2252 134.401 23.3801 134.214 22.3473C134.214 22.1595 134.214 22.0656 134.401 21.9717C134.589 21.9717 134.683 21.9717 134.777 22.1595C134.965 23.0984 135.246 23.9435 135.528 24.7886C135.528 24.9764 135.528 25.0703 135.34 25.1642C135.34 25.1642 135.34 25.1642 135.246 25.1642V25.2581Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M154.495 73.5211C162.101 72.9577 168.955 70.3286 168.955 70.3286L153.744 78.5915L154.495 73.5211Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M153.744 78.7794C153.744 78.7794 153.65 78.7794 153.556 78.7794C153.556 78.7794 153.462 78.5916 153.462 78.4977L154.214 73.4273C154.214 73.3334 154.307 73.2395 154.495 73.1456C162.007 72.5822 168.861 69.9531 168.955 69.9531C169.049 69.9531 169.237 69.9531 169.331 70.1409C169.331 70.2348 169.331 70.4226 169.237 70.5165L154.026 78.7794C154.026 78.7794 154.026 78.7794 153.932 78.7794H153.744ZM154.777 73.8029L154.12 78.0282L166.138 71.5494C163.415 72.3945 159.19 73.4273 154.777 73.8029Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M161.35 49.5773C161.162 49.5773 161.068 49.3895 161.162 49.2018C161.82 46.6665 163.416 45.8215 164.355 45.6337C165.669 45.2581 167.078 45.6337 167.923 46.5726C168.017 46.6665 168.017 46.8543 167.923 46.9482C167.829 47.0421 167.641 47.0421 167.547 46.9482C166.89 46.197 165.669 45.9154 164.543 46.1971C163.791 46.3849 162.383 47.0421 161.82 49.3895C161.82 49.4834 161.632 49.5773 161.538 49.5773H161.35Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M164.448 52.5823C164.448 52.5823 164.26 52.5823 164.26 52.4884C164.26 52.3945 164.26 52.2067 164.26 52.1128C166.514 50.4227 165.199 46.0095 165.199 45.9156C165.199 45.7278 165.199 45.6339 165.387 45.54C165.575 45.54 165.669 45.5401 165.763 45.7278C165.763 45.9156 167.265 50.6105 164.636 52.4884C164.636 52.4884 164.542 52.4884 164.448 52.4884V52.5823Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M131.022 51.3618L133.463 51.7374L130.928 53.3336L131.022 51.3618Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M130.928 53.6151C130.928 53.6151 130.834 53.6151 130.74 53.6151C130.74 53.6151 130.552 53.4274 130.646 53.3335L130.834 51.3616C130.834 51.3616 130.834 51.1738 130.928 51.1738C130.928 51.1738 131.022 51.1738 131.115 51.1738L133.463 51.4555C133.557 51.4555 133.651 51.5494 133.745 51.6433C133.838 51.7372 133.745 51.8311 133.651 51.925L131.115 53.6151C131.115 53.6151 131.022 53.6151 130.928 53.6151ZM131.303 51.7372V52.864L132.618 51.925L131.303 51.7372Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M167.547 14.2725C167.547 14.2725 168.205 26.479 173.463 31.5494C178.721 36.6199 193.557 40.1879 193.557 40.1879L190.928 32.9579L167.453 14.2725H167.547Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M193.65 40.3757C193.087 40.1879 178.721 36.8076 173.369 31.7372C168.111 26.5729 167.359 14.7419 167.359 14.2724C167.359 14.1785 167.359 14.0846 167.547 13.9907C167.641 13.9907 167.735 13.9907 167.829 13.9907L191.303 32.6762C191.303 32.6762 191.303 32.6762 191.303 32.7701L193.932 40.0001C193.932 40.094 193.932 40.1879 193.932 40.2818C193.932 40.2818 193.838 40.3757 193.744 40.3757H193.65ZM167.923 14.8358C168.111 17.371 169.331 27.0424 173.744 31.3616C178.345 35.7748 190.552 39.0611 193.181 39.7184L190.834 33.0518L167.923 14.7419V14.8358Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M165.2 6.29084C165.2 6.29084 163.979 16.2439 170.834 24.9763C177.688 33.7087 191.115 32.9575 191.115 32.9575C191.115 32.9575 204.824 15.7744 193.745 6.29084C177.594 -7.51198 165.2 6.29084 165.2 6.29084Z"
|
||||
fill="#FF9C0F"
|
||||
/>
|
||||
<path
|
||||
d="M190.27 33.2395C189.049 33.2395 186.42 33.2395 183.416 32.4883C179.566 31.6432 174.12 29.6714 170.552 25.1644C164.073 16.9015 164.73 7.32399 164.824 6.38503V6.19724C164.918 6.10334 177.594 -7.69948 193.744 6.19724C204.824 15.6808 191.209 33.0517 191.115 33.2395C191.115 33.2395 191.021 33.3334 190.927 33.3334C190.927 33.3334 190.646 33.3334 190.176 33.3334L190.27 33.2395ZM165.481 6.38503C165.388 7.41789 164.73 16.7137 171.021 24.7888C177.312 32.8639 189.519 32.6761 190.927 32.6761C192.054 31.2677 203.791 15.3052 193.557 6.57282C178.251 -6.47883 166.608 5.25827 165.481 6.47893V6.38503Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M194.12 27.5121C189.519 27.5121 176.937 23.7563 173.463 15.1178C173.463 14.93 173.463 14.8361 173.65 14.7422C173.838 14.7422 173.932 14.7422 174.026 14.93C177.594 23.5685 190.927 27.2304 194.683 26.9488C194.871 26.9488 194.965 27.0426 194.965 27.2304C194.965 27.4182 194.871 27.5121 194.683 27.5121C194.496 27.5121 194.308 27.5121 194.12 27.5121Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M327.547 78.0281C327.547 78.0281 316.843 189.577 289.895 203.005C262.946 216.526 234.777 185.915 234.777 185.915V209.953H103.228V187.136C103.228 187.136 51.7726 204.319 39.0965 176.15C26.4204 147.981 13.2749 78.0281 13.2749 78.0281L65.2937 77.8403L84.9181 138.122C84.9181 138.122 107.547 110.422 134.026 90.2347C160.411 70.0469 197.782 78.2159 214.026 93.3333C230.364 108.451 259.848 148.92 259.848 148.92L275.247 78.122H327.547V78.0281Z"
|
||||
fill="#FFFFFE"
|
||||
/>
|
||||
<path
|
||||
d="M234.777 210.235H103.228C103.04 210.235 102.946 210.141 102.946 209.953V187.512C100.693 188.169 88.862 191.831 75.9042 192.488C56.8432 193.521 44.0732 187.887 38.815 176.244C26.3267 148.357 13.0873 78.7793 12.9934 78.0281V77.8403C12.9934 77.8403 13.0873 77.7464 13.1812 77.7464L65.2 77.5586C65.2939 77.5586 65.3878 77.5586 65.4817 77.7464L85.0122 137.559C87.923 134.084 109.425 108.732 133.838 90.0468C161.069 69.2018 198.627 78.6854 214.214 93.1455C229.425 107.23 256.28 143.568 259.754 148.263L274.965 78.0281C274.965 77.9342 275.153 77.8403 275.247 77.8403H327.547C327.735 77.8403 327.829 77.9342 327.829 78.122C327.829 78.3098 327.829 78.122 327.829 78.2159C327.641 79.8121 324.918 107.136 319.097 135.587C311.022 174.929 301.256 197.746 289.989 203.38C265.106 215.868 238.909 190.704 234.965 186.76V210.047C234.965 210.235 234.871 210.329 234.684 210.329L234.777 210.235ZM103.698 210.235H234.59V185.915C234.59 185.821 234.59 185.728 234.777 185.634C234.871 185.634 234.965 185.634 235.059 185.634C235.341 185.915 263.416 215.868 289.895 202.629C315.716 189.765 326.702 84.6947 327.36 78.122H275.623L260.317 148.732C260.317 148.826 260.223 148.92 260.13 148.92C260.036 148.92 259.942 148.92 259.848 148.826C259.566 148.451 230.177 108.263 214.026 93.3332C198.533 78.967 161.256 69.6713 134.308 90.2346C108.205 110.235 85.4817 137.84 85.2939 138.122C85.2939 138.122 85.1061 138.216 85.0122 138.216C84.9183 138.216 84.8244 138.216 84.8244 138.028L65.2 77.9342L13.7446 78.122C14.7774 83.3802 27.4535 149.014 39.4723 175.868C51.8666 203.474 102.759 186.854 103.322 186.667C103.322 186.667 103.51 186.667 103.604 186.667C103.604 186.667 103.698 186.76 103.698 186.854V209.39V210.235Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M104.261 156.338C103.697 164.977 103.322 175.587 103.322 187.137L98.7209 188.263L104.355 156.338H104.261Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M98.7207 188.545C98.7207 188.545 98.6268 188.545 98.5329 188.545C98.5329 188.545 98.439 188.357 98.5329 188.263L104.073 156.432C104.073 156.338 104.073 156.244 104.261 156.15C104.355 156.15 104.448 156.15 104.542 156.15C104.542 156.15 104.636 156.338 104.542 156.432C103.885 166.949 103.603 177.277 103.603 187.136C103.603 196.995 103.603 187.418 103.416 187.418L98.8146 188.545H98.7207ZM103.697 161.221L99.0024 187.887L102.946 186.855C102.946 178.498 103.228 169.953 103.603 161.221H103.697Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M104.261 156.62C104.073 156.62 103.979 156.432 103.979 156.338C104.824 143.286 105.857 134.648 105.857 134.648C105.857 134.46 106.045 134.366 106.139 134.366C106.327 134.366 106.421 134.554 106.421 134.648C106.421 134.648 105.388 143.38 104.543 156.338C104.543 156.526 104.449 156.62 104.261 156.62Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M240.411 191.174L234.778 185.916C234.778 174.93 234.214 163.38 233.557 153.709L240.411 191.08V191.174Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M240.411 191.456C240.411 191.456 240.317 191.456 240.223 191.456L234.589 186.197C234.589 186.197 234.589 186.103 234.589 186.01C234.589 176.714 234.214 165.916 233.369 153.897C233.369 153.709 233.463 153.615 233.65 153.615C233.838 153.615 233.932 153.615 233.932 153.803L240.786 191.174C240.786 191.268 240.786 191.456 240.599 191.456C240.599 191.456 240.599 191.456 240.505 191.456H240.411ZM235.059 185.822L239.941 190.423L234.12 158.592C234.777 168.639 235.059 177.841 235.059 185.822Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M233.557 154.085C233.369 154.085 233.275 153.991 233.275 153.803C232.336 140.658 231.303 130.705 231.303 130.611C231.303 130.423 231.303 130.329 231.585 130.329C231.773 130.329 231.867 130.423 231.867 130.611C231.867 130.705 232.993 140.564 233.932 153.803C233.932 153.991 233.838 154.085 233.651 154.085H233.557Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M233.556 154.084C233.369 154.084 233.275 153.99 233.275 153.803C233.275 153.615 233.369 153.521 233.556 153.521C233.744 153.521 233.838 153.615 233.838 153.803C233.838 153.99 233.744 154.084 233.556 154.084Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M260.505 158.967C256.092 158.967 249.989 149.672 244.73 141.597C243.322 139.531 242.007 137.465 240.787 135.587C240.129 134.648 239.378 133.428 238.439 131.925C229.801 118.498 206.702 82.9112 172.054 82.9112C137.406 82.9112 169.425 82.9111 168.111 83.0989C138.909 85.3525 107.923 118.874 91.3971 136.808C86.1389 142.536 81.5379 147.418 80.505 147.418C78.0637 145.822 61.444 104.507 51.491 78.2163H65.106L84.9182 138.874L85.3877 138.31C85.5755 138.029 108.298 110.423 134.402 90.5168C144.637 82.7233 157.313 78.5919 171.209 78.5919C185.106 78.5919 204.073 84.5074 213.932 93.6154C229.989 108.545 259.472 148.733 259.754 149.108L260.317 149.953L275.81 78.498H292.618C289.237 80.0003 282.759 83.0989 281.726 86.3853C281.35 87.4182 280.505 91.6435 279.097 98.0285C274.965 117.09 266.327 156.808 262.007 158.78C261.632 158.967 261.162 159.061 260.787 159.061L260.505 158.967Z"
|
||||
fill="#D5C0FC"
|
||||
/>
|
||||
<path
|
||||
d="M13.2748 78.028L65.2935 77.8403C65.2935 77.8403 61.7255 68.3567 67.0776 53.1454C70.5518 43.2863 64.8241 39.906 61.256 41.1266C57.7818 42.3473 53.5565 48.3567 53.5565 48.3567C53.5565 48.3567 47.641 44.9764 43.6973 40.3755C39.7536 35.8684 38.9086 33.0515 36.0917 33.2393C33.2748 33.4271 31.5846 35.1172 32.3358 38.028C32.3358 38.028 30.1762 34.9294 26.2325 34.7417C22.2889 34.5539 22.3827 39.1548 22.3827 39.1548C22.3827 39.1548 16.5611 35.0233 12.7114 36.5257C8.86162 38.028 10.364 42.629 10.364 42.629C10.364 42.629 6.98369 38.028 3.41561 39.5304C-0.0585646 41.0327 -1.8426 48.075 2.94613 55.305C7.73486 62.5351 11.4907 68.9201 13.1809 78.2158L13.2748 78.028Z"
|
||||
fill="#F48282"
|
||||
/>
|
||||
<mask
|
||||
id="mask2_912_14740"
|
||||
style={{ maskType: "luminance" }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="33"
|
||||
width="69"
|
||||
height="46"
|
||||
>
|
||||
<path
|
||||
d="M13.2748 78.028L65.2935 77.8403C65.2935 77.8403 61.7255 68.3567 67.0776 53.1454C70.5518 43.2863 64.8241 39.906 61.256 41.1266C57.7818 42.3473 53.5565 48.3567 53.5565 48.3567C53.5565 48.3567 47.641 44.9764 43.6973 40.3755C39.7536 35.8684 38.9086 33.0515 36.0917 33.2393C33.3687 33.4271 31.5846 35.1172 32.3358 37.9341C32.3358 37.9341 30.1762 34.8356 26.2325 34.6478C22.3828 34.46 22.3827 39.0609 22.3827 39.0609C22.3827 39.0609 16.5611 34.9294 12.7114 36.4318C8.86162 37.9341 10.364 42.5351 10.364 42.5351C10.364 42.5351 6.98369 37.9341 3.41561 39.4365C-0.0585646 40.9388 -1.8426 47.9811 2.94613 55.2111C7.73486 62.4412 11.4907 68.8262 13.1809 78.1219L13.2748 78.028Z"
|
||||
fill="white"
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#mask2_912_14740)">
|
||||
<path
|
||||
d="M33.5564 33.2389C36.1856 33.2389 39.5658 39.6239 42.6644 43.4737C45.763 47.3234 48.1104 49.1075 47.641 50.7976C47.1715 52.4878 45.763 53.5206 45.763 53.5206C45.763 53.5206 54.0259 50.6098 53.5564 47.0418C53.087 43.4737 59.0964 38.3094 59.0964 38.3094C59.0964 38.3094 66.2325 40.8446 65.1057 45.915C63.979 50.9854 59.2842 56.2436 59.0964 63.8493C58.9086 71.4549 59.1903 77.5582 55.7161 80.2812C52.2419 82.9103 70.364 80.7507 70.364 80.7507L73.3687 59.8117L70.7395 34.5535L37.8757 26.5723L33.3687 33.3328L33.5564 33.2389Z"
|
||||
fill="#2973E8"
|
||||
/>
|
||||
<path
|
||||
d="M24.1669 34.2717C26.3265 34.6473 26.9838 36.1496 29.519 40.1872C32.0542 44.2247 34.965 48.0745 37.0307 49.3891C39.0965 50.7036 40.411 48.6379 39.2843 47.4172C38.1575 46.1966 35.6223 43.2858 34.4016 41.3139C33.181 39.3421 31.303 35.3984 31.303 35.3984L26.6082 31.6426L24.073 34.2717H24.1669Z"
|
||||
fill="#2973E8"
|
||||
/>
|
||||
<path
|
||||
d="M12.2423 36.0556C14.3081 36.0556 16.0921 38.3091 19.3785 42.5345C22.6649 46.7598 25.3879 49.7645 26.9841 50.7974C28.6743 51.8303 32.0545 51.3608 30.1766 49.2012C28.2048 47.0415 25.7635 44.4124 24.5428 42.5345C23.3222 40.6566 21.7259 38.0274 21.7259 38.0274L18.5334 33.896L12.1484 36.1495L12.2423 36.0556Z"
|
||||
fill="#2973E8"
|
||||
/>
|
||||
<path
|
||||
d="M2.5708 39.0605C5.01212 39.6239 5.01211 39.3422 7.54732 44.1309C10.0825 48.9197 12.6177 52.2999 15.0591 53.6145C17.5004 54.9291 21.3501 54.3657 18.2515 52.1121C15.153 49.8586 13.3689 47.2295 12.5239 45.8211C11.5849 44.4126 9.23747 39.3422 9.23747 39.3422L5.10601 36.9009L2.6647 38.9666L2.5708 39.0605Z"
|
||||
fill="#2973E8"
|
||||
/>
|
||||
<path
|
||||
d="M59.754 67.5112C59.5662 68.9197 59.1906 70.0464 58.7211 70.9854C56.3737 72.8633 53.369 73.7084 50.4582 74.3657C49.8948 74.4596 50.0826 75.3986 50.7399 75.3047C52.9934 74.8352 55.247 74.2718 57.2188 73.2389C54.4958 75.7741 49.1437 76.807 37.8761 78.4032C20.693 80.8446 60.1296 82.0652 60.1296 82.0652L60.9747 70.6098L59.754 67.699V67.5112Z"
|
||||
fill="#2973E8"
|
||||
/>
|
||||
<path
|
||||
d="M48.1105 74.4595C47.5471 74.4595 47.5471 75.3984 48.1105 75.3984C48.6739 75.3984 48.6739 74.4595 48.1105 74.4595Z"
|
||||
fill="#2973E8"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M13.2748 78.31H13.087C13.087 78.31 13.087 78.2161 13.087 78.1222C11.303 68.7325 7.45323 62.2537 2.94619 55.3053C0.317083 51.2678 -0.621889 46.7607 0.410975 43.1926C0.974356 41.2208 2.10112 39.8123 3.50957 39.249C6.23257 38.1222 8.86168 40.2818 10.0823 41.5964C9.89455 40.094 10.0823 37.371 12.8053 36.3382C16.1856 35.0236 20.8805 37.8405 22.2889 38.7795C22.2889 38.0283 22.6645 36.526 23.6974 35.4931C24.4485 34.8358 25.2936 34.5541 26.4204 34.5541C29.1434 34.648 31.1152 36.2443 32.0542 37.1832C32.0542 36.2443 32.242 35.4931 32.7114 34.8358C33.4626 33.803 34.6833 33.2396 36.2795 33.1457C38.3453 32.9579 39.472 34.4602 41.2561 36.8077C42.0072 37.8405 42.9462 38.9673 44.073 40.3757C47.5471 44.3194 52.5237 47.5119 53.6504 48.1692C54.4016 47.0424 58.0636 42.1598 61.3499 41.033C63.134 40.3757 65.1997 40.9391 66.7021 42.3476C68.0166 43.6621 69.8946 46.8546 67.5471 53.3335C62.195 68.2631 65.6692 77.7466 65.7631 77.8405C65.7631 77.8405 65.7631 78.0283 65.7631 78.1222C65.7631 78.2161 65.6692 78.2161 65.5753 78.2161L13.5565 78.4039L13.2748 78.31ZM5.01192 39.3429C4.54243 39.3429 4.16684 39.3429 3.69736 39.6246C2.4767 40.1879 1.44384 41.5025 0.880459 43.2865C-0.152405 46.7607 0.786561 51.08 3.32177 54.9297C7.82881 61.7842 11.7725 68.2631 13.5565 77.7466L64.918 77.5588C64.3546 75.587 62.1011 66.479 66.8898 53.0518C69.1434 46.8546 67.3593 43.8499 66.2326 42.6293C64.918 41.3147 63.134 40.8452 61.5377 41.4086C58.1575 42.6293 54.026 48.4509 54.026 48.5448C54.026 48.6386 53.7443 48.7325 53.6504 48.5448C53.6504 48.5448 47.641 45.0706 43.6974 40.5635C42.4767 39.1551 41.5377 37.9344 40.8805 36.9955C39.0964 34.648 38.1574 33.4274 36.3734 33.5213C34.965 33.6152 33.8382 34.1785 33.1809 35.0236C32.6175 35.7748 32.5237 36.8077 32.8053 37.9344C32.8053 38.0283 32.8053 38.2161 32.6175 38.2161C32.5236 38.2161 32.3359 38.2161 32.242 38.1222C32.242 38.1222 30.0823 35.1175 26.4204 34.9297C25.4814 34.9297 24.7302 35.1175 24.073 35.6809C22.8523 36.9015 22.7584 39.0612 22.7584 39.0612C22.7584 39.1551 22.7584 39.249 22.5706 39.3429C22.4767 39.3429 22.3828 39.3429 22.2889 39.3429C22.2889 39.3429 16.5612 35.3053 12.8992 36.7138C9.33117 38.1222 10.6457 42.2537 10.7396 42.4415C10.7396 42.5354 10.7396 42.7231 10.6457 42.817C10.5518 42.817 10.364 42.817 10.2701 42.817C10.2701 42.817 7.82882 39.5307 5.01192 39.5307V39.3429Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M38.5332 55.7746C38.4393 55.7746 38.2515 55.6807 38.2515 55.5868C38.2515 55.399 38.2515 55.3051 38.4393 55.3051C38.5332 55.3051 50.458 53.2393 53.2749 48.1689C53.2749 48.075 53.5566 47.9811 53.6505 48.075C53.7444 48.075 53.8383 48.3567 53.7444 48.4506C50.7397 53.7088 39.0027 55.7746 38.4393 55.7746H38.5332Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M40.2234 48.5447H40.0356C40.0356 48.5447 35.2469 43.9438 32.2422 38.1222C32.2422 38.0283 32.2422 37.8405 32.3361 37.7466C32.43 37.7466 32.6178 37.7466 32.7117 37.8405C35.7164 43.4743 40.4112 48.0752 40.4112 48.0752C40.5051 48.1691 40.5051 48.3569 40.4112 48.4508C40.4112 48.4508 40.3173 48.4508 40.2234 48.4508V48.5447Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M30.9276 50.2349H30.7398C30.7398 50.2349 24.6365 44.3194 22.2891 39.3429C22.2891 39.249 22.2891 39.0612 22.383 38.9673C22.4769 38.9673 22.6646 38.9673 22.7585 39.0612C25.106 43.9438 31.0215 49.7654 31.1154 49.8593C31.2093 49.9532 31.2093 50.141 31.1154 50.2349C31.1154 50.2349 31.0215 50.2349 30.9276 50.2349Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M19.6601 53.521C19.6601 53.521 19.5662 53.521 19.4723 53.521C19.4723 53.521 13.2751 49.1079 10.1765 42.629C10.1765 42.5351 10.1765 42.3473 10.2704 42.2534C10.3643 42.2534 10.5521 42.2534 10.646 42.3473C13.6507 48.6384 19.6601 52.9577 19.754 52.9577C19.8479 52.9577 19.9418 53.2393 19.754 53.3332L19.5662 53.4271L19.6601 53.521Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M327.547 78.028L275.528 77.8403C275.528 77.8403 279.096 68.3567 273.744 53.1454C270.27 43.2863 275.998 39.906 279.566 41.1266C283.04 42.3473 287.265 48.3567 287.265 48.3567C287.265 48.3567 293.181 44.9764 297.124 40.3755C301.068 35.8684 301.913 33.0515 304.73 33.2393C307.547 33.4271 309.237 35.1172 308.486 38.028C308.486 38.028 310.646 34.9294 314.589 34.7417C318.439 34.5539 318.439 39.1548 318.439 39.1548C318.439 39.1548 324.261 35.0233 328.11 36.5257C331.96 38.028 330.458 42.629 330.458 42.629C330.458 42.629 333.838 38.028 337.406 39.5304C340.88 41.0327 342.57 48.075 337.876 55.305C333.181 62.5351 329.331 68.9201 327.641 78.2158L327.547 78.028Z"
|
||||
fill="#F48282"
|
||||
/>
|
||||
<mask
|
||||
id="mask3_912_14740"
|
||||
style={{ maskType: "luminance" }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="272"
|
||||
y="33"
|
||||
width="69"
|
||||
height="46"
|
||||
>
|
||||
<path
|
||||
d="M327.547 78.028L275.528 77.8403C275.528 77.8403 279.096 68.3567 273.744 53.1454C270.27 43.2863 275.998 39.906 279.566 41.1266C283.04 42.3473 287.265 48.3567 287.265 48.3567C287.265 48.3567 293.181 44.9764 297.124 40.3755C301.068 35.8684 301.913 33.0515 304.73 33.2393C307.453 33.4271 309.237 35.1172 308.486 37.9341C308.486 37.9341 310.646 34.8356 314.589 34.6478C318.439 34.46 318.439 39.0609 318.439 39.0609C318.439 39.0609 324.261 34.9294 328.11 36.4318C331.96 37.9341 330.458 42.5351 330.458 42.5351C330.458 42.5351 333.838 37.9341 337.406 39.4365C340.88 40.9388 342.57 47.9811 337.876 55.2111C333.181 62.4412 329.331 68.8262 327.641 78.1219L327.547 78.028Z"
|
||||
fill="white"
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#mask3_912_14740)">
|
||||
<path
|
||||
d="M307.172 33.2389C304.542 33.2389 301.162 39.6239 298.064 43.4737C294.965 47.3234 292.618 49.1075 293.087 50.7976C293.557 52.4878 294.965 53.5206 294.965 53.5206C294.965 53.5206 286.702 50.6098 287.172 47.0418C287.641 43.4737 281.632 38.3094 281.632 38.3094C281.632 38.3094 274.496 40.8446 275.622 45.915C276.749 50.9854 281.444 56.2436 281.632 63.8493C281.819 71.4549 281.538 77.5582 285.012 80.2812C288.486 82.9103 270.364 80.7507 270.364 80.7507L267.359 59.8117L269.989 34.5535L302.852 26.5723L307.359 33.3328L307.172 33.2389Z"
|
||||
fill="#2973E8"
|
||||
/>
|
||||
<path
|
||||
d="M316.561 34.2717C314.401 34.6473 313.744 36.1496 311.115 40.1872C308.58 44.2247 305.669 48.0745 303.603 49.3891C301.538 50.7036 300.223 48.6379 301.35 47.4172C302.477 46.1966 305.012 43.2858 306.232 41.3139C307.453 39.3421 309.331 35.3984 309.331 35.3984L314.026 31.6426L316.561 34.2717Z"
|
||||
fill="#2973E8"
|
||||
/>
|
||||
<path
|
||||
d="M328.58 36.0556C326.514 36.0556 324.73 38.3091 321.444 42.5345C318.064 46.7598 315.435 49.7645 313.838 50.7974C312.242 51.8303 308.674 51.3608 310.646 49.2012C312.618 47.0415 315.059 44.4124 316.28 42.5345C317.5 40.6566 319.097 38.0274 319.097 38.0274L322.289 33.896L328.674 36.1495L328.58 36.0556Z"
|
||||
fill="#2973E8"
|
||||
/>
|
||||
<path
|
||||
d="M338.251 39.0605C335.81 39.6239 335.81 39.3422 333.275 44.1309C330.74 48.9197 328.204 52.2999 325.763 53.6145C323.322 54.9291 319.472 54.3657 322.571 52.1121C325.669 49.8586 327.453 47.2295 328.298 45.8211C329.143 44.4126 331.585 39.3422 331.585 39.3422L335.716 36.9009L338.157 38.9666L338.251 39.0605Z"
|
||||
fill="#2973E8"
|
||||
/>
|
||||
<path
|
||||
d="M280.974 67.5112C281.162 68.9197 281.444 70.0464 281.913 70.9854C284.261 72.8633 287.266 73.7084 290.176 74.3657C290.74 74.4596 290.552 75.3986 289.895 75.3047C287.641 74.8352 285.388 74.2718 283.416 73.2389C286.139 75.7741 291.491 76.807 302.759 78.4032C319.942 80.8446 280.505 82.0652 280.505 82.0652L279.66 70.6098L280.881 67.699L280.974 67.5112Z"
|
||||
fill="#2973E8"
|
||||
/>
|
||||
<path
|
||||
d="M292.618 74.4595C293.181 74.4595 293.181 75.3984 292.618 75.3984C292.054 75.3984 292.054 74.4595 292.618 74.4595Z"
|
||||
fill="#2973E8"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M327.547 78.3097L275.528 78.1219C275.528 78.1219 275.34 78.1219 275.34 78.028C275.34 77.9341 275.34 77.8402 275.34 77.7463C275.34 77.6524 278.815 68.1688 273.556 53.2392C271.209 46.6665 273.087 43.5679 274.401 42.2533C275.81 40.8449 277.97 40.2815 279.754 40.9388C282.946 42.0655 286.608 46.9482 287.453 48.0749C288.58 47.4176 293.556 44.319 297.031 40.2815C298.251 38.9669 299.096 37.7463 299.848 36.7134C301.632 34.366 302.758 32.8636 304.824 33.0514C306.42 33.1453 307.735 33.8026 308.392 34.7416C308.862 35.3988 309.049 36.2439 309.049 37.089C309.988 36.15 311.866 34.5538 314.683 34.4599C315.81 34.4599 316.749 34.7416 317.406 35.3989C318.439 36.4317 318.721 37.9341 318.815 38.6852C320.223 37.7463 324.918 34.9294 328.298 36.2439C331.115 37.3707 331.303 39.9998 331.021 41.5021C332.242 40.1876 334.871 37.9341 337.594 39.1547C339.002 39.7181 340.129 41.2205 340.693 43.0984C341.725 46.6665 340.786 51.2674 338.157 55.2111C333.65 62.0655 329.707 68.5444 328.017 78.028C328.017 78.028 328.017 78.1219 328.017 78.2158C328.017 78.2158 327.923 78.2158 327.829 78.2158L327.547 78.3097ZM275.904 77.5585L327.265 77.7463C329.049 68.2627 332.993 61.7838 337.5 54.9294C340.035 51.0796 340.974 46.6665 339.941 43.2862C339.378 41.5021 338.439 40.1876 337.125 39.6242C333.838 38.2158 330.552 42.6289 330.552 42.6289C330.552 42.7228 330.364 42.8167 330.176 42.6289C330.082 42.6289 329.988 42.4411 330.082 42.2533C330.082 42.0655 331.491 37.9341 327.923 36.5256C324.261 35.1172 318.627 39.1547 318.533 39.1547C318.533 39.1547 318.345 39.1547 318.251 39.1547C318.251 39.1547 318.063 38.9669 318.063 38.873C318.063 38.7791 318.063 36.7134 316.749 35.4927C316.186 34.9294 315.34 34.6477 314.401 34.7416C310.74 34.9294 308.58 37.9341 308.58 37.9341C308.58 38.028 308.392 38.1219 308.204 38.028C308.11 38.028 308.017 37.8402 308.017 37.7463C308.298 36.6195 308.204 35.5866 307.641 34.8355C306.984 33.9904 305.857 33.427 304.448 33.3331C302.664 33.2392 301.725 34.366 299.941 36.8073C299.19 37.7463 298.251 39.0608 297.125 40.3754C293.181 44.8824 287.265 48.3566 287.171 48.3566C287.078 48.3566 286.89 48.3566 286.796 48.3566C286.796 48.3566 282.664 42.4411 279.284 41.2205C277.688 40.6571 275.81 41.2205 274.589 42.4411C273.369 43.6618 271.678 46.6665 273.838 52.8636C278.627 66.2909 276.373 75.4928 275.81 77.3707L275.904 77.5585Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M302.195 55.7747C301.725 55.7747 289.894 53.6151 286.89 48.4508C286.89 48.3569 286.89 48.1691 286.984 48.0752C287.077 48.0752 287.265 48.0752 287.359 48.1691C290.176 53.1456 302.101 55.2113 302.195 55.3052C302.383 55.3052 302.477 55.493 302.383 55.5869C302.383 55.6808 302.195 55.7747 302.101 55.7747H302.195Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M300.599 48.5449H300.411C300.317 48.451 300.317 48.2632 300.411 48.1693C300.411 48.1693 305.106 43.5684 308.111 37.9346C308.111 37.8407 308.392 37.7468 308.486 37.8406C308.58 37.8406 308.674 38.1223 308.58 38.2162C305.576 43.9439 300.787 48.5449 300.787 48.6388C300.787 48.6388 300.693 48.6388 300.599 48.6388V48.5449Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M309.894 50.2347H309.707C309.613 50.1408 309.613 49.953 309.707 49.8591C309.707 49.8591 315.716 44.0375 318.063 39.061C318.063 38.9671 318.345 38.8732 318.439 38.9671C318.533 38.9671 318.627 39.2488 318.533 39.3427C316.092 44.3192 310.082 50.2347 310.082 50.2347C310.082 50.2347 309.988 50.2347 309.894 50.2347Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
<path
|
||||
d="M321.162 53.521C321.162 53.521 320.975 53.521 320.975 53.4271C320.975 53.3332 320.975 53.1454 320.975 53.0515C320.975 53.0515 327.078 48.7323 330.083 42.4412C330.083 42.2534 330.364 42.2534 330.458 42.3473C330.552 42.3473 330.646 42.629 330.552 42.7229C327.453 49.2018 321.35 53.521 321.256 53.6149C321.256 53.6149 321.162 53.6149 321.069 53.6149L321.162 53.521Z"
|
||||
fill="#101720"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Text variant="h4" className="text-center text-[1.375rem]">
|
||||
No triggers yet
|
||||
</Text>
|
||||
<Text variant="large" className="text-zinc-700">
|
||||
Set up automatic triggers for your agent to run tasks automatically —
|
||||
they'll show up here.
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { GraphExecution } from "@/app/api/__generated__/models/graphExecution";
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
import { ClockClockwiseIcon } from "@phosphor-icons/react";
|
||||
import moment from "moment";
|
||||
import { AGENT_LIBRARY_SECTION_PADDING_X } from "../../../helpers";
|
||||
import { RunStatusBadge } from "../SelectedRunView/components/RunStatusBadge";
|
||||
@@ -20,7 +21,20 @@ export function RunDetailHeader({ agent, run, scheduleRecurrence }: Props) {
|
||||
<div className="flex w-full flex-col gap-0">
|
||||
<div className="flex w-full flex-col flex-wrap items-start justify-between gap-1 md:flex-row md:items-center">
|
||||
<div className="flex min-w-0 flex-1 flex-col items-start gap-3">
|
||||
{run?.status ? <RunStatusBadge status={run.status} /> : null}
|
||||
{run?.status ? (
|
||||
<RunStatusBadge status={run.status} />
|
||||
) : scheduleRecurrence ? (
|
||||
<div className="inline-flex items-center gap-1 rounded-md bg-yellow-50 p-1">
|
||||
<ClockClockwiseIcon
|
||||
size={16}
|
||||
className="text-yellow-700"
|
||||
weight="bold"
|
||||
/>
|
||||
<Text variant="small-medium" className="text-yellow-700">
|
||||
Scheduled
|
||||
</Text>
|
||||
</div>
|
||||
) : null}
|
||||
<Text variant="h2" className="truncate text-ellipsis">
|
||||
{agent.name}
|
||||
</Text>
|
||||
|
||||
@@ -24,6 +24,7 @@ import { SelectedViewLayout } from "../SelectedViewLayout";
|
||||
import { RunOutputs } from "./components/RunOutputs";
|
||||
import { RunSummary } from "./components/RunSummary";
|
||||
import { SelectedRunActions } from "./components/SelectedRunActions/SelectedRunActions";
|
||||
import { WebhookTriggerSection } from "./components/WebhookTriggerSection";
|
||||
import { useSelectedRunView } from "./useSelectedRunView";
|
||||
|
||||
const anchorStyles =
|
||||
@@ -42,10 +43,8 @@ export function SelectedRunView({
|
||||
onSelectRun,
|
||||
onClearSelectedRun,
|
||||
}: Props) {
|
||||
const { run, isLoading, responseError, httpError } = useSelectedRunView(
|
||||
agent.graph_id,
|
||||
runId,
|
||||
);
|
||||
const { run, preset, isLoading, responseError, httpError } =
|
||||
useSelectedRunView(agent.graph_id, runId);
|
||||
|
||||
const {
|
||||
pendingReviews,
|
||||
@@ -90,6 +89,16 @@ export function SelectedRunView({
|
||||
<div className="flex flex-col gap-4">
|
||||
<RunDetailHeader agent={agent} run={run} />
|
||||
|
||||
{preset &&
|
||||
agent.trigger_setup_info &&
|
||||
preset.webhook_id &&
|
||||
preset.webhook && (
|
||||
<WebhookTriggerSection
|
||||
preset={preset}
|
||||
triggerSetupInfo={agent.trigger_setup_info}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Navigation Links */}
|
||||
<div className={AGENT_LIBRARY_SECTION_PADDING_X}>
|
||||
<nav className="flex gap-8 px-3 pb-1">
|
||||
@@ -135,7 +144,7 @@ export function SelectedRunView({
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<InfoIcon
|
||||
size={8}
|
||||
size={16}
|
||||
className="cursor-help text-neutral-500 hover:text-neutral-700"
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
"use client";
|
||||
|
||||
import { GraphExecution } from "@/app/api/__generated__/models/graphExecution";
|
||||
import { Button } from "@/components/atoms/Button/Button";
|
||||
import { Input } from "@/components/atoms/Input/Input";
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
import { Dialog } from "@/components/molecules/Dialog/Dialog";
|
||||
import { useState } from "react";
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onCreate: (name: string, description: string) => Promise<void>;
|
||||
run?: GraphExecution;
|
||||
}
|
||||
|
||||
export function CreateTemplateModal({ isOpen, onClose, onCreate }: Props) {
|
||||
const [name, setName] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!name.trim()) return;
|
||||
|
||||
setIsCreating(true);
|
||||
try {
|
||||
await onCreate(name.trim(), description.trim());
|
||||
setName("");
|
||||
setDescription("");
|
||||
onClose();
|
||||
} finally {
|
||||
setIsCreating(false);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
setName("");
|
||||
setDescription("");
|
||||
onClose();
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
controlled={{ isOpen, set: () => onClose() }}
|
||||
styling={{ maxWidth: "500px" }}
|
||||
>
|
||||
<Dialog.Content>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text variant="lead" as="h2" className="!font-medium !text-black">
|
||||
Create Template
|
||||
</Text>
|
||||
<Text variant="body" className="text-zinc-600">
|
||||
Save this task as a template to reuse later with the same inputs
|
||||
and credentials.
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<div className="flex w-[96%] flex-col gap-4 pl-1">
|
||||
<Input
|
||||
id="template-name"
|
||||
label="Name"
|
||||
placeholder="Enter template name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
<Input
|
||||
type="textarea"
|
||||
id="template-description"
|
||||
label="Description"
|
||||
placeholder="Optional description"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Dialog.Footer className="mt-6">
|
||||
<div className="flex justify-end gap-3">
|
||||
<Button variant="secondary" onClick={handleCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={handleSubmit}
|
||||
disabled={!name.trim() || isCreating}
|
||||
loading={isCreating}
|
||||
>
|
||||
Create Template
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog.Footer>
|
||||
</Dialog.Content>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -7,11 +7,13 @@ import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag";
|
||||
import {
|
||||
ArrowBendLeftUpIcon,
|
||||
ArrowBendRightDownIcon,
|
||||
CardsThreeIcon,
|
||||
EyeIcon,
|
||||
StopIcon,
|
||||
} from "@phosphor-icons/react";
|
||||
import { AgentActionsDropdown } from "../../../AgentActionsDropdown";
|
||||
import { ShareRunButton } from "../../../ShareRunButton/ShareRunButton";
|
||||
import { CreateTemplateModal } from "../CreateTemplateModal/CreateTemplateModal";
|
||||
import { useSelectedRunActions } from "./useSelectedRunActions";
|
||||
|
||||
type Props = {
|
||||
@@ -30,9 +32,13 @@ export function SelectedRunActions(props: Props) {
|
||||
canStop,
|
||||
isStopping,
|
||||
openInBuilderHref,
|
||||
handleCreateTemplate,
|
||||
isCreateTemplateModalOpen,
|
||||
setIsCreateTemplateModalOpen,
|
||||
} = useSelectedRunActions({
|
||||
agentGraphId: props.agent.graph_id,
|
||||
run: props.run,
|
||||
agent: props.agent,
|
||||
onSelectRun: props.onSelectRun,
|
||||
onClearSelectedRun: props.onClearSelectedRun,
|
||||
});
|
||||
@@ -107,12 +113,27 @@ export function SelectedRunActions(props: Props) {
|
||||
variant="white"
|
||||
fullWidth={false}
|
||||
/>
|
||||
<Button
|
||||
variant="icon"
|
||||
size="icon"
|
||||
aria-label="Save task as template"
|
||||
onClick={() => setIsCreateTemplateModalOpen(true)}
|
||||
title="Create template"
|
||||
>
|
||||
<CardsThreeIcon weight="bold" size={18} className="text-zinc-700" />
|
||||
</Button>
|
||||
<AgentActionsDropdown
|
||||
agent={props.agent}
|
||||
run={props.run}
|
||||
agentGraphId={props.agent.graph_id}
|
||||
onClearSelectedRun={props.onClearSelectedRun}
|
||||
/>
|
||||
<CreateTemplateModal
|
||||
isOpen={isCreateTemplateModalOpen}
|
||||
onClose={() => setIsCreateTemplateModalOpen(false)}
|
||||
onCreate={handleCreateTemplate}
|
||||
run={props.run}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,12 @@ import {
|
||||
usePostV1ExecuteGraphAgent,
|
||||
usePostV1StopGraphExecution,
|
||||
} from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import {
|
||||
getGetV2ListPresetsQueryKey,
|
||||
usePostV2CreateANewPreset,
|
||||
} 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 { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
@@ -13,6 +18,7 @@ import { useState } from "react";
|
||||
interface Args {
|
||||
agentGraphId: string;
|
||||
run?: GraphExecution;
|
||||
agent?: LibraryAgent;
|
||||
onSelectRun?: (id: string) => void;
|
||||
onClearSelectedRun?: () => void;
|
||||
}
|
||||
@@ -22,6 +28,8 @@ export function useSelectedRunActions(args: Args) {
|
||||
const { toast } = useToast();
|
||||
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||
const [isCreateTemplateModalOpen, setIsCreateTemplateModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const canStop =
|
||||
args.run?.status === "RUNNING" || args.run?.status === "QUEUED";
|
||||
@@ -32,6 +40,9 @@ export function useSelectedRunActions(args: Args) {
|
||||
const { mutateAsync: executeRun, isPending: isRunningAgain } =
|
||||
usePostV1ExecuteGraphAgent();
|
||||
|
||||
const { mutateAsync: createPreset, isPending: isCreatingTemplate } =
|
||||
usePostV2CreateANewPreset();
|
||||
|
||||
async function handleStopRun() {
|
||||
try {
|
||||
await stopRun({
|
||||
@@ -106,6 +117,52 @@ export function useSelectedRunActions(args: Args) {
|
||||
setShowDeleteDialog(open);
|
||||
}
|
||||
|
||||
async function handleCreateTemplate(name: string, description: string) {
|
||||
if (!args.run) {
|
||||
toast({
|
||||
title: "Run not found",
|
||||
description: "Cannot create template from missing run",
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await createPreset({
|
||||
data: {
|
||||
name,
|
||||
description,
|
||||
graph_execution_id: args.run.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
toast({
|
||||
title: "Template created",
|
||||
});
|
||||
|
||||
if (args.agent) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getGetV2ListPresetsQueryKey({
|
||||
graph_id: args.agent.graph_id,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
setIsCreateTemplateModalOpen(false);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
toast({
|
||||
title: "Failed to create template",
|
||||
description:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "An unexpected error occurred.",
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Open in builder URL helper
|
||||
const openInBuilderHref = args.run
|
||||
? `/build?flowID=${args.run.graph_id}&flowVersion=${args.run.graph_version}&flowExecutionID=${args.run.id}`
|
||||
@@ -120,5 +177,9 @@ export function useSelectedRunActions(args: Args) {
|
||||
handleShowDeleteDialog,
|
||||
handleStopRun,
|
||||
handleRunAgain,
|
||||
handleCreateTemplate,
|
||||
isCreatingTemplate,
|
||||
isCreateTemplateModalOpen,
|
||||
setIsCreateTemplateModalOpen,
|
||||
} as const;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
"use client";
|
||||
|
||||
import { GraphTriggerInfo } from "@/app/api/__generated__/models/graphTriggerInfo";
|
||||
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||
import { Button } from "@/components/atoms/Button/Button";
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
import { CopyIcon } from "@phosphor-icons/react";
|
||||
import { RunDetailCard } from "../../RunDetailCard/RunDetailCard";
|
||||
|
||||
interface Props {
|
||||
preset: LibraryAgentPreset;
|
||||
triggerSetupInfo: GraphTriggerInfo;
|
||||
}
|
||||
|
||||
function getTriggerStatus(
|
||||
preset: LibraryAgentPreset,
|
||||
): "active" | "inactive" | "broken" {
|
||||
if (!preset.webhook_id || !preset.webhook) return "broken";
|
||||
return preset.is_active ? "active" : "inactive";
|
||||
}
|
||||
|
||||
export function WebhookTriggerSection({ preset, triggerSetupInfo }: Props) {
|
||||
const status = getTriggerStatus(preset);
|
||||
const webhook = preset.webhook;
|
||||
|
||||
function handleCopyWebhookUrl() {
|
||||
if (webhook?.url) {
|
||||
navigator.clipboard.writeText(webhook.url);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<RunDetailCard title="Trigger Status">
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Text variant="large-medium">Status</Text>
|
||||
<span
|
||||
className={`rounded-full px-2 py-0.5 text-xs font-medium ${
|
||||
status === "active"
|
||||
? "bg-green-100 text-green-800"
|
||||
: status === "inactive"
|
||||
? "bg-yellow-100 text-yellow-800"
|
||||
: "bg-red-100 text-red-800"
|
||||
}`}
|
||||
>
|
||||
{status === "active"
|
||||
? "Active"
|
||||
: status === "inactive"
|
||||
? "Inactive"
|
||||
: "Broken"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{!preset.webhook_id ? (
|
||||
<Text variant="body" className="text-red-600">
|
||||
This trigger is not attached to a webhook. Use "Set up
|
||||
trigger" to fix this.
|
||||
</Text>
|
||||
) : !triggerSetupInfo.credentials_input_name && webhook ? (
|
||||
<div className="flex flex-col gap-2">
|
||||
<Text variant="body">
|
||||
This trigger is ready to be used. Use the Webhook URL below to set
|
||||
up the trigger connection with the service of your choosing.
|
||||
</Text>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text variant="small-medium">Webhook URL:</Text>
|
||||
<div className="flex gap-2 rounded-md bg-gray-50 p-2">
|
||||
<code className="flex-1 select-all text-sm">{webhook.url}</code>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="size-7 flex-none p-1"
|
||||
onClick={handleCopyWebhookUrl}
|
||||
title="Copy webhook URL"
|
||||
>
|
||||
<CopyIcon className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Text variant="body" className="text-muted-foreground">
|
||||
This agent trigger is{" "}
|
||||
{preset.is_active
|
||||
? "ready. When a trigger is received, it will run with the provided settings."
|
||||
: "disabled. It will not respond to triggers until you enable it."}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
</RunDetailCard>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { useGetV1GetExecutionDetails } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import type { GetV1GetExecutionDetails200 } from "@/app/api/__generated__/models/getV1GetExecutionDetails200";
|
||||
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, {
|
||||
@@ -37,6 +40,18 @@ export function useSelectedRunView(graphId: string, runId: string) {
|
||||
? (query.data?.data as GetV1GetExecutionDetails200)
|
||||
: undefined;
|
||||
|
||||
const presetId =
|
||||
run && "preset_id" in run && run.preset_id
|
||||
? (run.preset_id as string)
|
||||
: undefined;
|
||||
|
||||
const presetQuery = useGetV2GetASpecificPreset(presetId || "", {
|
||||
query: {
|
||||
enabled: !!presetId,
|
||||
select: (res) => okData<LibraryAgentPreset>(res),
|
||||
},
|
||||
});
|
||||
|
||||
const httpError =
|
||||
status && status !== 200
|
||||
? { status, statusText: `Request failed: ${status}` }
|
||||
@@ -44,8 +59,9 @@ export function useSelectedRunView(graphId: string, runId: string) {
|
||||
|
||||
return {
|
||||
run,
|
||||
isLoading: query.isLoading,
|
||||
responseError: query.error,
|
||||
preset: presetQuery.data,
|
||||
isLoading: query.isLoading || presetQuery.isLoading,
|
||||
responseError: query.error || presetQuery.error,
|
||||
httpError,
|
||||
} as const;
|
||||
}
|
||||
|
||||
@@ -83,19 +83,17 @@ export function SelectedScheduleView({
|
||||
<div className="flex min-h-0 min-w-0 flex-1 flex-col">
|
||||
<SelectedViewLayout agentName={agent.name} agentId={agent.id}>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex w-full flex-col gap-0">
|
||||
<RunDetailHeader
|
||||
agent={agent}
|
||||
run={undefined}
|
||||
scheduleRecurrence={
|
||||
schedule
|
||||
? `${humanizeCronExpression(schedule.cron || "")} · ${getTimezoneDisplayName(schedule.timezone || userTzRes || "UTC")}`
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex w-full flex-col gap-0">
|
||||
<RunDetailHeader
|
||||
agent={agent}
|
||||
run={undefined}
|
||||
scheduleRecurrence={
|
||||
schedule
|
||||
? `${humanizeCronExpression(schedule.cron || "")} · ${getTimezoneDisplayName(schedule.timezone || userTzRes || "UTC")}`
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
"use client";
|
||||
|
||||
import type { GraphExecutionMeta } from "@/app/api/__generated__/models/graphExecutionMeta";
|
||||
import type { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { Input } from "@/components/atoms/Input/Input";
|
||||
import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard";
|
||||
import { InformationTooltip } from "@/components/molecules/InformationTooltip/InformationTooltip";
|
||||
import {
|
||||
getAgentCredentialsFields,
|
||||
getAgentInputFields,
|
||||
} from "../../modals/AgentInputsReadOnly/helpers";
|
||||
import { CredentialsInput } from "../../modals/CredentialsInputs/CredentialsInputs";
|
||||
import { RunAgentInputs } from "../../modals/RunAgentInputs/RunAgentInputs";
|
||||
import { LoadingSelectedContent } from "../LoadingSelectedContent";
|
||||
import { RunDetailCard } from "../RunDetailCard/RunDetailCard";
|
||||
import { RunDetailHeader } from "../RunDetailHeader/RunDetailHeader";
|
||||
import { SelectedViewLayout } from "../SelectedViewLayout";
|
||||
import { SelectedTemplateActions } from "./components/SelectedTemplateActions";
|
||||
import { WebhookTriggerCard } from "./components/WebhookTriggerCard";
|
||||
import { useSelectedTemplateView } from "./useSelectedTemplateView";
|
||||
|
||||
interface Props {
|
||||
agent: LibraryAgent;
|
||||
templateId: string;
|
||||
onClearSelectedRun?: () => void;
|
||||
onRunCreated?: (execution: GraphExecutionMeta) => void;
|
||||
onSwitchToRunsTab?: () => void;
|
||||
}
|
||||
|
||||
export function SelectedTemplateView({
|
||||
agent,
|
||||
templateId,
|
||||
onClearSelectedRun,
|
||||
onRunCreated,
|
||||
onSwitchToRunsTab,
|
||||
}: Props) {
|
||||
const {
|
||||
template,
|
||||
isLoading,
|
||||
error,
|
||||
name,
|
||||
setName,
|
||||
description,
|
||||
setDescription,
|
||||
inputs,
|
||||
setInputValue,
|
||||
credentials,
|
||||
setCredentialValue,
|
||||
handleSaveChanges,
|
||||
handleStartTask,
|
||||
isSaving,
|
||||
isStarting,
|
||||
} = useSelectedTemplateView({
|
||||
templateId,
|
||||
graphId: agent.graph_id,
|
||||
onRunCreated,
|
||||
});
|
||||
|
||||
const agentInputFields = getAgentInputFields(agent);
|
||||
const agentCredentialsFields = getAgentCredentialsFields(agent);
|
||||
const inputFields = Object.entries(agentInputFields);
|
||||
const credentialFields = Object.entries(agentCredentialsFields);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<ErrorCard
|
||||
responseError={
|
||||
error
|
||||
? {
|
||||
message: String(
|
||||
(error as unknown as { message?: string })?.message ||
|
||||
"Failed to load template",
|
||||
),
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
httpError={
|
||||
(error as any)?.status
|
||||
? {
|
||||
status: (error as any).status,
|
||||
statusText: (error as any).statusText,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
context="template"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading && !template) {
|
||||
return <LoadingSelectedContent agentName={agent.name} agentId={agent.id} />;
|
||||
}
|
||||
|
||||
if (!template) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hasWebhook = !!template.webhook_id && template.webhook;
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full gap-4">
|
||||
<div className="flex min-h-0 min-w-0 flex-1 flex-col">
|
||||
<SelectedViewLayout agentName={agent.name} agentId={agent.id}>
|
||||
<div className="flex flex-col gap-4">
|
||||
<RunDetailHeader agent={agent} run={undefined} />
|
||||
|
||||
{hasWebhook && agent.trigger_setup_info && (
|
||||
<WebhookTriggerCard
|
||||
template={template}
|
||||
triggerSetupInfo={agent.trigger_setup_info}
|
||||
/>
|
||||
)}
|
||||
|
||||
<RunDetailCard title="Template Details">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Input
|
||||
id="template-name"
|
||||
label="Name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="Enter template name"
|
||||
/>
|
||||
|
||||
<Input
|
||||
id="template-description"
|
||||
label="Description"
|
||||
type="textarea"
|
||||
rows={3}
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
placeholder="Enter template description"
|
||||
/>
|
||||
</div>
|
||||
</RunDetailCard>
|
||||
|
||||
{inputFields.length > 0 && (
|
||||
<RunDetailCard title="Your Input">
|
||||
<div className="flex flex-col gap-4">
|
||||
{inputFields.map(([key, inputSubSchema]) => (
|
||||
<div
|
||||
key={key}
|
||||
className="flex w-full flex-col gap-0 space-y-2"
|
||||
>
|
||||
<label className="flex items-center gap-1 text-sm font-medium">
|
||||
{inputSubSchema.title || key}
|
||||
{inputSubSchema.description && (
|
||||
<InformationTooltip
|
||||
description={inputSubSchema.description}
|
||||
/>
|
||||
)}
|
||||
</label>
|
||||
<RunAgentInputs
|
||||
schema={inputSubSchema}
|
||||
value={inputs[key] ?? inputSubSchema.default}
|
||||
placeholder={inputSubSchema.description}
|
||||
onChange={(value) => setInputValue(key, value)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</RunDetailCard>
|
||||
)}
|
||||
|
||||
{credentialFields.length > 0 && (
|
||||
<RunDetailCard title="Task Credentials">
|
||||
<div className="flex flex-col gap-6">
|
||||
{credentialFields.map(([key, inputSubSchema]) => (
|
||||
<CredentialsInput
|
||||
key={key}
|
||||
schema={
|
||||
{ ...inputSubSchema, discriminator: undefined } as any
|
||||
}
|
||||
selectedCredentials={
|
||||
credentials[key] ?? inputSubSchema.default
|
||||
}
|
||||
onSelectCredentials={(value) =>
|
||||
setCredentialValue(key, value!)
|
||||
}
|
||||
siblingInputs={inputs}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</RunDetailCard>
|
||||
)}
|
||||
</div>
|
||||
</SelectedViewLayout>
|
||||
</div>
|
||||
{template ? (
|
||||
<div className="-mt-2 max-w-[3.75rem] flex-shrink-0">
|
||||
<SelectedTemplateActions
|
||||
agent={agent}
|
||||
templateId={template.id}
|
||||
onDeleted={onClearSelectedRun}
|
||||
onSaveChanges={handleSaveChanges}
|
||||
onStartTask={handleStartTask}
|
||||
isSaving={isSaving}
|
||||
isStarting={isStarting}
|
||||
onSwitchToRunsTab={onSwitchToRunsTab}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
getGetV2ListPresetsQueryKey,
|
||||
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";
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
import { Dialog } from "@/components/molecules/Dialog/Dialog";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { FloppyDiskIcon, PlayIcon, TrashIcon } from "@phosphor-icons/react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
|
||||
interface Props {
|
||||
agent: LibraryAgent;
|
||||
templateId: string;
|
||||
onDeleted?: () => void;
|
||||
onSaveChanges?: () => void;
|
||||
onStartTask?: () => void;
|
||||
isSaving?: boolean;
|
||||
isStarting?: boolean;
|
||||
onSwitchToRunsTab?: () => void;
|
||||
}
|
||||
|
||||
export function SelectedTemplateActions({
|
||||
agent,
|
||||
templateId,
|
||||
onDeleted,
|
||||
onSaveChanges,
|
||||
onStartTask,
|
||||
isSaving,
|
||||
isStarting,
|
||||
onSwitchToRunsTab,
|
||||
}: Props) {
|
||||
const { toast } = useToast();
|
||||
const queryClient = useQueryClient();
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||
|
||||
const deleteMutation = useDeleteV2DeleteAPreset({
|
||||
mutation: {
|
||||
onSuccess: async () => {
|
||||
toast({
|
||||
title: "Template deleted",
|
||||
});
|
||||
const queryKey = getGetV2ListPresetsQueryKey({
|
||||
graph_id: agent.graph_id,
|
||||
});
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey,
|
||||
});
|
||||
|
||||
const queryData = queryClient.getQueryData<{
|
||||
data: LibraryAgentPresetResponse;
|
||||
}>(queryKey);
|
||||
|
||||
const presets =
|
||||
okData<LibraryAgentPresetResponse>(queryData)?.presets ?? [];
|
||||
const templates = presets.filter(
|
||||
(preset) => !preset.webhook_id || !preset.webhook,
|
||||
);
|
||||
|
||||
setShowDeleteDialog(false);
|
||||
onDeleted?.();
|
||||
|
||||
if (templates.length === 0 && onSwitchToRunsTab) {
|
||||
onSwitchToRunsTab();
|
||||
}
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast({
|
||||
title: "Failed to delete template",
|
||||
description: error.message || "An unexpected error occurred.",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function handleDelete() {
|
||||
deleteMutation.mutate({ presetId: templateId });
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="my-4 flex flex-col items-center gap-3">
|
||||
<Button
|
||||
variant="icon"
|
||||
size="icon"
|
||||
aria-label="Save changes"
|
||||
onClick={onSaveChanges}
|
||||
disabled={isSaving || isStarting || deleteMutation.isPending}
|
||||
>
|
||||
{isSaving ? (
|
||||
<LoadingSpinner size="small" />
|
||||
) : (
|
||||
<FloppyDiskIcon weight="bold" size={18} className="text-zinc-700" />
|
||||
)}
|
||||
</Button>
|
||||
{onStartTask && (
|
||||
<Button
|
||||
variant="icon"
|
||||
size="icon"
|
||||
aria-label="Start task from template"
|
||||
onClick={onStartTask}
|
||||
disabled={isSaving || isStarting || deleteMutation.isPending}
|
||||
>
|
||||
{isStarting ? (
|
||||
<>
|
||||
<LoadingSpinner size="small" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PlayIcon weight="bold" size={16} />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="icon"
|
||||
size="icon"
|
||||
aria-label="Delete template"
|
||||
onClick={() => setShowDeleteDialog(true)}
|
||||
disabled={isSaving || isStarting || deleteMutation.isPending}
|
||||
>
|
||||
{deleteMutation.isPending ? (
|
||||
<LoadingSpinner size="small" />
|
||||
) : (
|
||||
<TrashIcon weight="bold" size={18} />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Dialog
|
||||
controlled={{
|
||||
isOpen: showDeleteDialog,
|
||||
set: setShowDeleteDialog,
|
||||
}}
|
||||
styling={{ maxWidth: "32rem" }}
|
||||
title="Delete template"
|
||||
>
|
||||
<Dialog.Content>
|
||||
<Text variant="large">
|
||||
Are you sure you want to delete this template? This action cannot be
|
||||
undone.
|
||||
</Text>
|
||||
<Dialog.Footer>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => setShowDeleteDialog(false)}
|
||||
disabled={deleteMutation.isPending}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={handleDelete}
|
||||
loading={deleteMutation.isPending}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Dialog.Footer>
|
||||
</Dialog.Content>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
"use client";
|
||||
|
||||
import { GraphTriggerInfo } from "@/app/api/__generated__/models/graphTriggerInfo";
|
||||
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||
import { Button } from "@/components/atoms/Button/Button";
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
import { CopyIcon } from "@phosphor-icons/react";
|
||||
import { RunDetailCard } from "../../RunDetailCard/RunDetailCard";
|
||||
|
||||
interface Props {
|
||||
template: LibraryAgentPreset;
|
||||
triggerSetupInfo: GraphTriggerInfo;
|
||||
}
|
||||
|
||||
function getTriggerStatus(
|
||||
template: LibraryAgentPreset,
|
||||
): "active" | "inactive" | "broken" {
|
||||
if (!template.webhook_id || !template.webhook) return "broken";
|
||||
return template.is_active ? "active" : "inactive";
|
||||
}
|
||||
|
||||
export function WebhookTriggerCard({ template, triggerSetupInfo }: Props) {
|
||||
const status = getTriggerStatus(template);
|
||||
const webhook = template.webhook;
|
||||
|
||||
function handleCopyWebhookUrl() {
|
||||
if (webhook?.url) {
|
||||
navigator.clipboard.writeText(webhook.url);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<RunDetailCard title="Trigger Status">
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Text variant="large-medium">Status</Text>
|
||||
<span
|
||||
className={`rounded-full px-2 py-0.5 text-xs font-medium ${
|
||||
status === "active"
|
||||
? "bg-green-100 text-green-800"
|
||||
: status === "inactive"
|
||||
? "bg-yellow-100 text-yellow-800"
|
||||
: "bg-red-100 text-red-800"
|
||||
}`}
|
||||
>
|
||||
{status === "active"
|
||||
? "Active"
|
||||
: status === "inactive"
|
||||
? "Inactive"
|
||||
: "Broken"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{!template.webhook_id ? (
|
||||
<Text variant="body" className="text-red-600">
|
||||
This trigger is not attached to a webhook. Use "Set up
|
||||
trigger" to fix this.
|
||||
</Text>
|
||||
) : !triggerSetupInfo.credentials_input_name && webhook ? (
|
||||
<div className="flex flex-col gap-2">
|
||||
<Text variant="body">
|
||||
This trigger is ready to be used. Use the Webhook URL below to set
|
||||
up the trigger connection with the service of your choosing.
|
||||
</Text>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text variant="body-medium">Webhook URL:</Text>
|
||||
<div className="flex gap-2 rounded-md bg-gray-50 p-2">
|
||||
<code className="flex-1 select-all text-sm">{webhook.url}</code>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="size-7 flex-none p-1"
|
||||
onClick={handleCopyWebhookUrl}
|
||||
title="Copy webhook URL"
|
||||
>
|
||||
<CopyIcon className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Text variant="body" className="text-muted-foreground">
|
||||
This agent trigger is{" "}
|
||||
{template.is_active
|
||||
? "ready. When a trigger is received, it will run with the provided settings."
|
||||
: "disabled. It will not respond to triggers until you enable it."}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
</RunDetailCard>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
"use client";
|
||||
|
||||
import { getGetV1ListGraphExecutionsInfiniteQueryOptions } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import {
|
||||
getGetV2GetASpecificPresetQueryKey,
|
||||
getGetV2ListPresetsQueryKey,
|
||||
useGetV2GetASpecificPreset,
|
||||
usePatchV2UpdateAnExistingPreset,
|
||||
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";
|
||||
import type { CredentialsMetaInput } from "@/lib/autogpt-server-api/types";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type Args = {
|
||||
templateId: string;
|
||||
graphId: string;
|
||||
onRunCreated?: (execution: GraphExecutionMeta) => void;
|
||||
};
|
||||
|
||||
export function useSelectedTemplateView({
|
||||
templateId,
|
||||
graphId,
|
||||
onRunCreated,
|
||||
}: Args) {
|
||||
const { toast } = useToast();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const query = useGetV2GetASpecificPreset(templateId, {
|
||||
query: {
|
||||
enabled: !!templateId,
|
||||
select: (res) => okData<LibraryAgentPreset>(res),
|
||||
},
|
||||
});
|
||||
|
||||
const [name, setName] = useState<string>("");
|
||||
const [description, setDescription] = useState<string>("");
|
||||
const [inputs, setInputs] = useState<Record<string, any>>({});
|
||||
const [credentials, setCredentials] = useState<
|
||||
Record<string, CredentialsMetaInput>
|
||||
>({});
|
||||
|
||||
useEffect(() => {
|
||||
if (query.data) {
|
||||
setName(query.data.name || "");
|
||||
setDescription(query.data.description || "");
|
||||
setInputs(query.data.inputs || {});
|
||||
setCredentials(query.data.credentials || {});
|
||||
}
|
||||
}, [query.data]);
|
||||
|
||||
const updateMutation = usePatchV2UpdateAnExistingPreset({
|
||||
mutation: {
|
||||
onSuccess: (response) => {
|
||||
if (response.status === 200) {
|
||||
toast({
|
||||
title: "Template updated",
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getGetV2GetASpecificPresetQueryKey(templateId),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getGetV2ListPresetsQueryKey({ graph_id: graphId }),
|
||||
});
|
||||
}
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast({
|
||||
title: "Failed to update template",
|
||||
description: error.message || "An unexpected error occurred.",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const executeMutation = usePostV2ExecuteAPreset({
|
||||
mutation: {
|
||||
onSuccess: (response) => {
|
||||
if (response.status === 200) {
|
||||
const execution = okData<GraphExecutionMeta>(response);
|
||||
if (execution) {
|
||||
toast({
|
||||
title: "Task started",
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey:
|
||||
getGetV1ListGraphExecutionsInfiniteQueryOptions(graphId)
|
||||
.queryKey,
|
||||
});
|
||||
onRunCreated?.(execution);
|
||||
}
|
||||
}
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast({
|
||||
title: "Failed to start task",
|
||||
description: error.message || "An unexpected error occurred.",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function handleSaveChanges() {
|
||||
if (!query.data) return;
|
||||
|
||||
const updateData: LibraryAgentPresetUpdatable = {};
|
||||
if (name !== (query.data.name || "")) {
|
||||
updateData.name = name;
|
||||
}
|
||||
|
||||
if (description !== (query.data.description || "")) {
|
||||
updateData.description = description;
|
||||
}
|
||||
|
||||
const inputsChanged =
|
||||
JSON.stringify(inputs) !== JSON.stringify(query.data.inputs || {});
|
||||
|
||||
const credentialsChanged =
|
||||
JSON.stringify(credentials) !==
|
||||
JSON.stringify(query.data.credentials || {});
|
||||
|
||||
if (inputsChanged || credentialsChanged) {
|
||||
updateData.inputs = inputs;
|
||||
updateData.credentials = credentials;
|
||||
}
|
||||
|
||||
updateMutation.mutate({
|
||||
presetId: templateId,
|
||||
data: updateData,
|
||||
});
|
||||
}
|
||||
|
||||
function handleStartTask() {
|
||||
executeMutation.mutate({
|
||||
presetId: templateId,
|
||||
data: {
|
||||
inputs: {},
|
||||
credential_inputs: {},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function setInputValue(key: string, value: any) {
|
||||
setInputs((prev) => ({ ...prev, [key]: value }));
|
||||
}
|
||||
|
||||
function setCredentialValue(key: string, value: CredentialsMetaInput) {
|
||||
setCredentials((prev) => ({ ...prev, [key]: value }));
|
||||
}
|
||||
|
||||
const httpError =
|
||||
query.isSuccess && !query.data
|
||||
? { status: 404, statusText: "Not found" }
|
||||
: undefined;
|
||||
|
||||
useEffect(() => {
|
||||
if (updateMutation.isSuccess && query.data) {
|
||||
setName(query.data.name || "");
|
||||
setDescription(query.data.description || "");
|
||||
setInputs(query.data.inputs || {});
|
||||
setCredentials(query.data.credentials || {});
|
||||
}
|
||||
}, [updateMutation.isSuccess, query.data]);
|
||||
|
||||
return {
|
||||
template: query.data,
|
||||
isLoading: query.isLoading,
|
||||
error: query.error || httpError,
|
||||
name,
|
||||
setName,
|
||||
description,
|
||||
setDescription,
|
||||
inputs,
|
||||
setInputValue,
|
||||
credentials,
|
||||
setCredentialValue,
|
||||
handleSaveChanges,
|
||||
handleStartTask,
|
||||
isSaving: updateMutation.isPending,
|
||||
isStarting: executeMutation.isPending,
|
||||
} as const;
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
"use client";
|
||||
|
||||
import type { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { Input } from "@/components/atoms/Input/Input";
|
||||
import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard";
|
||||
import { InformationTooltip } from "@/components/molecules/InformationTooltip/InformationTooltip";
|
||||
import {
|
||||
getAgentCredentialsFields,
|
||||
getAgentInputFields,
|
||||
} from "../../modals/AgentInputsReadOnly/helpers";
|
||||
import { CredentialsInput } from "../../modals/CredentialsInputs/CredentialsInputs";
|
||||
import { RunAgentInputs } from "../../modals/RunAgentInputs/RunAgentInputs";
|
||||
import { LoadingSelectedContent } from "../LoadingSelectedContent";
|
||||
import { RunDetailCard } from "../RunDetailCard/RunDetailCard";
|
||||
import { RunDetailHeader } from "../RunDetailHeader/RunDetailHeader";
|
||||
import { WebhookTriggerCard } from "../SelectedTemplateView/components/WebhookTriggerCard";
|
||||
import { SelectedViewLayout } from "../SelectedViewLayout";
|
||||
import { SelectedTriggerActions } from "./components/SelectedTriggerActions";
|
||||
import { useSelectedTriggerView } from "./useSelectedTriggerView";
|
||||
|
||||
interface Props {
|
||||
agent: LibraryAgent;
|
||||
triggerId: string;
|
||||
onClearSelectedRun?: () => void;
|
||||
onSwitchToRunsTab?: () => void;
|
||||
}
|
||||
|
||||
export function SelectedTriggerView({
|
||||
agent,
|
||||
triggerId,
|
||||
onClearSelectedRun,
|
||||
onSwitchToRunsTab,
|
||||
}: Props) {
|
||||
const {
|
||||
trigger,
|
||||
isLoading,
|
||||
error,
|
||||
name,
|
||||
setName,
|
||||
description,
|
||||
setDescription,
|
||||
inputs,
|
||||
setInputValue,
|
||||
credentials,
|
||||
setCredentialValue,
|
||||
handleSaveChanges,
|
||||
isSaving,
|
||||
} = useSelectedTriggerView({
|
||||
triggerId,
|
||||
graphId: agent.graph_id,
|
||||
});
|
||||
|
||||
const agentInputFields = getAgentInputFields(agent);
|
||||
const agentCredentialsFields = getAgentCredentialsFields(agent);
|
||||
const inputFields = Object.entries(agentInputFields);
|
||||
const credentialFields = Object.entries(agentCredentialsFields);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<ErrorCard
|
||||
responseError={
|
||||
error
|
||||
? {
|
||||
message: String(
|
||||
(error as unknown as { message?: string })?.message ||
|
||||
"Failed to load trigger",
|
||||
),
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
httpError={
|
||||
(error as any)?.status
|
||||
? {
|
||||
status: (error as any).status,
|
||||
statusText: (error as any).statusText,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
context="trigger"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading && !trigger) {
|
||||
return <LoadingSelectedContent agentName={agent.name} agentId={agent.id} />;
|
||||
}
|
||||
|
||||
if (!trigger) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hasWebhook = !!trigger.webhook_id && trigger.webhook;
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full gap-4">
|
||||
<div className="flex min-h-0 min-w-0 flex-1 flex-col">
|
||||
<SelectedViewLayout agentName={agent.name} agentId={agent.id}>
|
||||
<div className="flex flex-col gap-4">
|
||||
<RunDetailHeader agent={agent} run={undefined} />
|
||||
|
||||
<RunDetailCard title="Trigger Details">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Input
|
||||
id="trigger-name"
|
||||
label="Name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="Enter trigger name"
|
||||
/>
|
||||
|
||||
<Input
|
||||
id="trigger-description"
|
||||
label="Description"
|
||||
type="textarea"
|
||||
rows={3}
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
placeholder="Enter trigger description"
|
||||
/>
|
||||
</div>
|
||||
</RunDetailCard>
|
||||
|
||||
{hasWebhook && agent.trigger_setup_info && (
|
||||
<WebhookTriggerCard
|
||||
template={trigger}
|
||||
triggerSetupInfo={agent.trigger_setup_info}
|
||||
/>
|
||||
)}
|
||||
|
||||
{inputFields.length > 0 && (
|
||||
<RunDetailCard title="Your Input">
|
||||
<div className="flex flex-col gap-4">
|
||||
{inputFields.map(([key, inputSubSchema]) => (
|
||||
<div
|
||||
key={key}
|
||||
className="flex w-full flex-col gap-0 space-y-2"
|
||||
>
|
||||
<label className="flex items-center gap-1 text-sm font-medium">
|
||||
{inputSubSchema.title || key}
|
||||
{inputSubSchema.description && (
|
||||
<InformationTooltip
|
||||
description={inputSubSchema.description}
|
||||
/>
|
||||
)}
|
||||
</label>
|
||||
<RunAgentInputs
|
||||
schema={inputSubSchema}
|
||||
value={inputs[key] ?? inputSubSchema.default}
|
||||
placeholder={inputSubSchema.description}
|
||||
onChange={(value) => setInputValue(key, value)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</RunDetailCard>
|
||||
)}
|
||||
|
||||
{credentialFields.length > 0 && (
|
||||
<RunDetailCard title="Task Credentials">
|
||||
<div className="flex flex-col gap-6">
|
||||
{credentialFields.map(([key, inputSubSchema]) => (
|
||||
<CredentialsInput
|
||||
key={key}
|
||||
schema={
|
||||
{ ...inputSubSchema, discriminator: undefined } as any
|
||||
}
|
||||
selectedCredentials={
|
||||
credentials[key] ?? inputSubSchema.default
|
||||
}
|
||||
onSelectCredentials={(value) =>
|
||||
setCredentialValue(key, value!)
|
||||
}
|
||||
siblingInputs={inputs}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</RunDetailCard>
|
||||
)}
|
||||
</div>
|
||||
</SelectedViewLayout>
|
||||
</div>
|
||||
{trigger ? (
|
||||
<div className="-mt-2 max-w-[3.75rem] flex-shrink-0">
|
||||
<SelectedTriggerActions
|
||||
agent={agent}
|
||||
triggerId={trigger.id}
|
||||
onDeleted={onClearSelectedRun}
|
||||
onSaveChanges={handleSaveChanges}
|
||||
isSaving={isSaving}
|
||||
onSwitchToRunsTab={onSwitchToRunsTab}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
getGetV2ListPresetsQueryKey,
|
||||
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";
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
import { Dialog } from "@/components/molecules/Dialog/Dialog";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { FloppyDiskIcon, TrashIcon } from "@phosphor-icons/react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
|
||||
interface Props {
|
||||
agent: LibraryAgent;
|
||||
triggerId: string;
|
||||
onDeleted?: () => void;
|
||||
onSaveChanges?: () => void;
|
||||
isSaving?: boolean;
|
||||
onSwitchToRunsTab?: () => void;
|
||||
}
|
||||
|
||||
export function SelectedTriggerActions({
|
||||
agent,
|
||||
triggerId,
|
||||
onDeleted,
|
||||
onSaveChanges,
|
||||
isSaving,
|
||||
onSwitchToRunsTab,
|
||||
}: Props) {
|
||||
const { toast } = useToast();
|
||||
const queryClient = useQueryClient();
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||
|
||||
const deleteMutation = useDeleteV2DeleteAPreset({
|
||||
mutation: {
|
||||
onSuccess: async () => {
|
||||
toast({
|
||||
title: "Trigger deleted",
|
||||
});
|
||||
const queryKey = getGetV2ListPresetsQueryKey({
|
||||
graph_id: agent.graph_id,
|
||||
});
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey,
|
||||
});
|
||||
|
||||
const queryData = queryClient.getQueryData<{
|
||||
data: LibraryAgentPresetResponse;
|
||||
}>(queryKey);
|
||||
|
||||
const presets =
|
||||
okData<LibraryAgentPresetResponse>(queryData)?.presets ?? [];
|
||||
const triggers = presets.filter(
|
||||
(preset) => preset.webhook_id && preset.webhook,
|
||||
);
|
||||
|
||||
setShowDeleteDialog(false);
|
||||
onDeleted?.();
|
||||
|
||||
if (triggers.length === 0 && onSwitchToRunsTab) {
|
||||
onSwitchToRunsTab();
|
||||
}
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast({
|
||||
title: "Failed to delete trigger",
|
||||
description: error.message || "An unexpected error occurred.",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function handleDelete() {
|
||||
deleteMutation.mutate({ presetId: triggerId });
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="my-4 flex flex-col items-center gap-3">
|
||||
<Button
|
||||
variant="icon"
|
||||
size="icon"
|
||||
aria-label="Save changes"
|
||||
onClick={onSaveChanges}
|
||||
disabled={isSaving || deleteMutation.isPending}
|
||||
>
|
||||
{isSaving ? (
|
||||
<LoadingSpinner size="small" />
|
||||
) : (
|
||||
<FloppyDiskIcon weight="bold" size={18} className="text-zinc-700" />
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="icon"
|
||||
size="icon"
|
||||
aria-label="Delete trigger"
|
||||
onClick={() => setShowDeleteDialog(true)}
|
||||
disabled={isSaving || deleteMutation.isPending}
|
||||
>
|
||||
{deleteMutation.isPending ? (
|
||||
<LoadingSpinner size="small" />
|
||||
) : (
|
||||
<TrashIcon weight="bold" size={18} />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Dialog
|
||||
controlled={{
|
||||
isOpen: showDeleteDialog,
|
||||
set: setShowDeleteDialog,
|
||||
}}
|
||||
styling={{ maxWidth: "32rem" }}
|
||||
title="Delete trigger"
|
||||
>
|
||||
<Dialog.Content>
|
||||
<Text variant="large">
|
||||
Are you sure you want to delete this trigger? This action cannot be
|
||||
undone.
|
||||
</Text>
|
||||
<Dialog.Footer>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => setShowDeleteDialog(false)}
|
||||
disabled={deleteMutation.isPending}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={handleDelete}
|
||||
loading={deleteMutation.isPending}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Dialog.Footer>
|
||||
</Dialog.Content>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
getGetV2GetASpecificPresetQueryKey,
|
||||
getGetV2ListPresetsQueryKey,
|
||||
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";
|
||||
import type { CredentialsMetaInput } from "@/lib/autogpt-server-api/types";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type Args = {
|
||||
triggerId: string;
|
||||
graphId: string;
|
||||
};
|
||||
|
||||
export function useSelectedTriggerView({ triggerId, graphId }: Args) {
|
||||
const { toast } = useToast();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const query = useGetV2GetASpecificPreset(triggerId, {
|
||||
query: {
|
||||
enabled: !!triggerId,
|
||||
select: (res) => okData<LibraryAgentPreset>(res),
|
||||
},
|
||||
});
|
||||
|
||||
const [name, setName] = useState<string>("");
|
||||
const [description, setDescription] = useState<string>("");
|
||||
const [inputs, setInputs] = useState<Record<string, any>>({});
|
||||
const [credentials, setCredentials] = useState<
|
||||
Record<string, CredentialsMetaInput>
|
||||
>({});
|
||||
|
||||
useEffect(() => {
|
||||
if (query.data) {
|
||||
setName(query.data.name || "");
|
||||
setDescription(query.data.description || "");
|
||||
setInputs(query.data.inputs || {});
|
||||
setCredentials(query.data.credentials || {});
|
||||
}
|
||||
}, [query.data]);
|
||||
|
||||
const updateMutation = usePatchV2UpdateAnExistingPreset({
|
||||
mutation: {
|
||||
onSuccess: (response) => {
|
||||
if (response.status === 200) {
|
||||
toast({
|
||||
title: "Trigger updated",
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getGetV2GetASpecificPresetQueryKey(triggerId),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getGetV2ListPresetsQueryKey({ graph_id: graphId }),
|
||||
});
|
||||
}
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast({
|
||||
title: "Failed to update trigger",
|
||||
description: error.message || "An unexpected error occurred.",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function handleSaveChanges() {
|
||||
if (!query.data) return;
|
||||
|
||||
const updateData: LibraryAgentPresetUpdatable = {};
|
||||
if (name !== (query.data.name || "")) {
|
||||
updateData.name = name;
|
||||
}
|
||||
|
||||
if (description !== (query.data.description || "")) {
|
||||
updateData.description = description;
|
||||
}
|
||||
|
||||
const inputsChanged =
|
||||
JSON.stringify(inputs) !== JSON.stringify(query.data.inputs || {});
|
||||
|
||||
const credentialsChanged =
|
||||
JSON.stringify(credentials) !==
|
||||
JSON.stringify(query.data.credentials || {});
|
||||
|
||||
if (inputsChanged || credentialsChanged) {
|
||||
updateData.inputs = inputs;
|
||||
updateData.credentials = credentials;
|
||||
}
|
||||
|
||||
updateMutation.mutate({
|
||||
presetId: triggerId,
|
||||
data: updateData,
|
||||
});
|
||||
}
|
||||
|
||||
function setInputValue(key: string, value: any) {
|
||||
setInputs((prev) => ({ ...prev, [key]: value }));
|
||||
}
|
||||
|
||||
function setCredentialValue(key: string, value: CredentialsMetaInput) {
|
||||
setCredentials((prev) => ({ ...prev, [key]: value }));
|
||||
}
|
||||
|
||||
const httpError =
|
||||
query.isSuccess && !query.data
|
||||
? { status: 404, statusText: "Not found" }
|
||||
: undefined;
|
||||
|
||||
useEffect(() => {
|
||||
if (updateMutation.isSuccess && query.data) {
|
||||
setName(query.data.name || "");
|
||||
setDescription(query.data.description || "");
|
||||
setInputs(query.data.inputs || {});
|
||||
setCredentials(query.data.credentials || {});
|
||||
}
|
||||
}, [updateMutation.isSuccess, query.data]);
|
||||
|
||||
return {
|
||||
trigger: query.data,
|
||||
isLoading: query.isLoading,
|
||||
error: query.error || httpError,
|
||||
name,
|
||||
setName,
|
||||
description,
|
||||
setDescription,
|
||||
inputs,
|
||||
setInputValue,
|
||||
credentials,
|
||||
setCredentialValue,
|
||||
handleSaveChanges,
|
||||
isSaving: updateMutation.isPending,
|
||||
} as const;
|
||||
}
|
||||
@@ -16,17 +16,24 @@ import { cn } from "@/lib/utils";
|
||||
import { AGENT_LIBRARY_SECTION_PADDING_X } from "../../../helpers";
|
||||
import { RunListItem } from "./components/RunListItem";
|
||||
import { ScheduleListItem } from "./components/ScheduleListItem";
|
||||
import { TemplateListItem } from "./components/TemplateListItem";
|
||||
import { TriggerListItem } from "./components/TriggerListItem";
|
||||
import { useSidebarRunsList } from "./useSidebarRunsList";
|
||||
|
||||
interface Props {
|
||||
agent: LibraryAgent;
|
||||
selectedRunId?: string;
|
||||
onSelectRun: (id: string, tab?: "runs" | "scheduled") => void;
|
||||
onSelectRun: (
|
||||
id: string,
|
||||
tab?: "runs" | "scheduled" | "templates" | "triggers",
|
||||
) => void;
|
||||
onClearSelectedRun?: () => void;
|
||||
onTabChange?: (tab: "runs" | "scheduled" | "templates") => void;
|
||||
onTabChange?: (tab: "runs" | "scheduled" | "templates" | "triggers") => void;
|
||||
onCountsChange?: (info: {
|
||||
runsCount: number;
|
||||
schedulesCount: number;
|
||||
templatesCount: number;
|
||||
triggersCount: number;
|
||||
loading?: boolean;
|
||||
}) => void;
|
||||
}
|
||||
@@ -42,8 +49,12 @@ export function SidebarRunsList({
|
||||
const {
|
||||
runs,
|
||||
schedules,
|
||||
templates,
|
||||
triggers,
|
||||
runsCount,
|
||||
schedulesCount,
|
||||
templatesCount,
|
||||
triggersCount,
|
||||
error,
|
||||
loading,
|
||||
fetchMoreRuns,
|
||||
@@ -79,7 +90,7 @@ export function SidebarRunsList({
|
||||
<TabsLine
|
||||
value={tabValue}
|
||||
onValueChange={(v) => {
|
||||
const value = v as "runs" | "scheduled" | "templates";
|
||||
const value = v as "runs" | "scheduled" | "templates" | "triggers";
|
||||
onTabChange?.(value);
|
||||
if (value === "runs") {
|
||||
if (runs && runs.length) {
|
||||
@@ -95,6 +106,8 @@ export function SidebarRunsList({
|
||||
}
|
||||
} else if (value === "templates") {
|
||||
onClearSelectedRun?.();
|
||||
} else if (value === "triggers") {
|
||||
onClearSelectedRun?.();
|
||||
}
|
||||
}}
|
||||
className="flex min-h-0 flex-col overflow-hidden"
|
||||
@@ -106,8 +119,13 @@ export function SidebarRunsList({
|
||||
<TabsLineTrigger value="scheduled">
|
||||
Scheduled <span className="ml-3 inline-block">{schedulesCount}</span>
|
||||
</TabsLineTrigger>
|
||||
{triggersCount > 0 && (
|
||||
<TabsLineTrigger value="triggers">
|
||||
Triggers <span className="ml-3 inline-block">{triggersCount}</span>
|
||||
</TabsLineTrigger>
|
||||
)}
|
||||
<TabsLineTrigger value="templates">
|
||||
Templates <span className="ml-3 inline-block">0</span>
|
||||
Templates <span className="ml-3 inline-block">{templatesCount}</span>
|
||||
</TabsLineTrigger>
|
||||
</TabsLineList>
|
||||
|
||||
@@ -165,6 +183,35 @@ export function SidebarRunsList({
|
||||
)}
|
||||
</div>
|
||||
</TabsLineContent>
|
||||
{triggersCount > 0 && (
|
||||
<TabsLineContent
|
||||
value="triggers"
|
||||
className={cn(
|
||||
"mt-0 flex min-h-0 flex-1 flex-col",
|
||||
AGENT_LIBRARY_SECTION_PADDING_X,
|
||||
)}
|
||||
>
|
||||
<div className="flex h-full flex-nowrap items-center justify-start gap-4 overflow-x-scroll px-1 pb-4 pt-1 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-zinc-300 lg:flex-col lg:gap-3 lg:overflow-y-auto lg:overflow-x-hidden">
|
||||
{triggers.length > 0 ? (
|
||||
triggers.map((trigger) => (
|
||||
<div className="w-[15rem] lg:w-full" key={trigger.id}>
|
||||
<TriggerListItem
|
||||
trigger={trigger}
|
||||
selected={selectedRunId === trigger.id}
|
||||
onClick={() => onSelectRun(trigger.id, "triggers")}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="flex min-h-[50vh] flex-col items-center justify-center">
|
||||
<Text variant="large" className="text-zinc-700">
|
||||
No triggers set up
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TabsLineContent>
|
||||
)}
|
||||
<TabsLineContent
|
||||
value="templates"
|
||||
className={cn(
|
||||
@@ -173,11 +220,23 @@ export function SidebarRunsList({
|
||||
)}
|
||||
>
|
||||
<div className="flex h-full flex-nowrap items-center justify-start gap-4 overflow-x-scroll px-1 pb-4 pt-1 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-zinc-300 lg:flex-col lg:gap-3 lg:overflow-y-auto lg:overflow-x-hidden">
|
||||
<div className="flex min-h-[50vh] flex-col items-center justify-center">
|
||||
<Text variant="large" className="text-zinc-700">
|
||||
No templates saved
|
||||
</Text>
|
||||
</div>
|
||||
{templates.length > 0 ? (
|
||||
templates.map((template) => (
|
||||
<div className="w-[15rem] lg:w-full" key={template.id}>
|
||||
<TemplateListItem
|
||||
template={template}
|
||||
selected={selectedRunId === template.id}
|
||||
onClick={() => onSelectRun(template.id, "templates")}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="flex min-h-[50vh] flex-col items-center justify-center">
|
||||
<Text variant="large" className="text-zinc-700">
|
||||
No templates saved
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TabsLineContent>
|
||||
</>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { GraphExecutionJobInfo } from "@/app/api/__generated__/models/graphExecutionJobInfo";
|
||||
import moment from "moment";
|
||||
import { RunSidebarCard } from "./RunSidebarCard";
|
||||
import { IconWrapper } from "./RunIconWrapper";
|
||||
import { ClockClockwiseIcon } from "@phosphor-icons/react";
|
||||
import moment from "moment";
|
||||
import { IconWrapper } from "./RunIconWrapper";
|
||||
import { RunSidebarCard } from "./RunSidebarCard";
|
||||
|
||||
interface ScheduleListItemProps {
|
||||
schedule: GraphExecutionJobInfo;
|
||||
@@ -25,10 +24,10 @@ export function ScheduleListItem({
|
||||
onClick={onClick}
|
||||
selected={selected}
|
||||
icon={
|
||||
<IconWrapper className="border-slate-50 bg-slate-50">
|
||||
<IconWrapper className="border-slate-50 bg-yellow-50">
|
||||
<ClockClockwiseIcon
|
||||
size={16}
|
||||
className="text-slate-700"
|
||||
className="text-yellow-700"
|
||||
weight="bold"
|
||||
/>
|
||||
</IconWrapper>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
"use client";
|
||||
|
||||
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||
import { FileTextIcon } from "@phosphor-icons/react";
|
||||
import moment from "moment";
|
||||
import { IconWrapper } from "./RunIconWrapper";
|
||||
import { RunSidebarCard } from "./RunSidebarCard";
|
||||
|
||||
interface TemplateListItemProps {
|
||||
template: LibraryAgentPreset;
|
||||
selected?: boolean;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export function TemplateListItem({
|
||||
template,
|
||||
selected,
|
||||
onClick,
|
||||
}: TemplateListItemProps) {
|
||||
return (
|
||||
<RunSidebarCard
|
||||
icon={
|
||||
<IconWrapper className="border-blue-50 bg-blue-50">
|
||||
<FileTextIcon size={16} className="text-zinc-700" weight="bold" />
|
||||
</IconWrapper>
|
||||
}
|
||||
title={template.name}
|
||||
description={moment(template.updated_at).fromNow()}
|
||||
onClick={onClick}
|
||||
selected={selected}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
"use client";
|
||||
|
||||
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||
import { LightningIcon } from "@phosphor-icons/react";
|
||||
import moment from "moment";
|
||||
import { IconWrapper } from "./RunIconWrapper";
|
||||
import { RunSidebarCard } from "./RunSidebarCard";
|
||||
|
||||
interface TriggerListItemProps {
|
||||
trigger: LibraryAgentPreset;
|
||||
selected?: boolean;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export function TriggerListItem({
|
||||
trigger,
|
||||
selected,
|
||||
onClick,
|
||||
}: TriggerListItemProps) {
|
||||
return (
|
||||
<RunSidebarCard
|
||||
icon={
|
||||
<IconWrapper className="border-purple-50 bg-purple-50">
|
||||
<LightningIcon size={16} className="text-zinc-700" weight="bold" />
|
||||
</IconWrapper>
|
||||
}
|
||||
title={trigger.name}
|
||||
description={moment(trigger.updated_at).fromNow()}
|
||||
onClick={onClick}
|
||||
selected={selected}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -3,8 +3,10 @@
|
||||
import { useEffect, useMemo } from "react";
|
||||
|
||||
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";
|
||||
@@ -15,8 +17,15 @@ import {
|
||||
getNextRunsPageParam,
|
||||
} from "./helpers";
|
||||
|
||||
function parseTab(value: string | null): "runs" | "scheduled" | "templates" {
|
||||
if (value === "runs" || value === "scheduled" || value === "templates") {
|
||||
function parseTab(
|
||||
value: string | null,
|
||||
): "runs" | "scheduled" | "templates" | "triggers" {
|
||||
if (
|
||||
value === "runs" ||
|
||||
value === "scheduled" ||
|
||||
value === "templates" ||
|
||||
value === "triggers"
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
return "runs";
|
||||
@@ -24,10 +33,15 @@ function parseTab(value: string | null): "runs" | "scheduled" | "templates" {
|
||||
|
||||
type Args = {
|
||||
graphId?: string;
|
||||
onSelectRun: (runId: string, tab?: "runs" | "scheduled") => void;
|
||||
onSelectRun: (
|
||||
runId: string,
|
||||
tab?: "runs" | "scheduled" | "templates" | "triggers",
|
||||
) => void;
|
||||
onCountsChange?: (info: {
|
||||
runsCount: number;
|
||||
schedulesCount: number;
|
||||
templatesCount: number;
|
||||
triggersCount: number;
|
||||
loading?: boolean;
|
||||
}) => void;
|
||||
};
|
||||
@@ -67,16 +81,40 @@ export function useSidebarRunsList({
|
||||
},
|
||||
);
|
||||
|
||||
const presetsQuery = useGetV2ListPresets(
|
||||
{ graph_id: graphId || null, page: 1, page_size: 100 },
|
||||
{
|
||||
query: {
|
||||
enabled: !!graphId,
|
||||
select: (r) => okData<LibraryAgentPresetResponse>(r)?.presets ?? [],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const runs = useMemo(
|
||||
() => extractRunsFromPages(runsQuery.data),
|
||||
[runsQuery.data],
|
||||
);
|
||||
|
||||
const schedules = schedulesQuery.data || [];
|
||||
const allPresets = presetsQuery.data || [];
|
||||
const triggers = useMemo(
|
||||
() => allPresets.filter((preset) => preset.webhook_id && preset.webhook),
|
||||
[allPresets],
|
||||
);
|
||||
const templates = useMemo(
|
||||
() => allPresets.filter((preset) => !preset.webhook_id || !preset.webhook),
|
||||
[allPresets],
|
||||
);
|
||||
|
||||
const runsCount = computeRunsCount(runsQuery.data, runs.length);
|
||||
const schedulesCount = schedules.length;
|
||||
const loading = !schedulesQuery.isSuccess || !runsQuery.isSuccess;
|
||||
const templatesCount = templates.length;
|
||||
const triggersCount = triggers.length;
|
||||
const loading =
|
||||
!schedulesQuery.isSuccess ||
|
||||
!runsQuery.isSuccess ||
|
||||
!presetsQuery.isSuccess;
|
||||
|
||||
// Update query cache when execution events arrive via websocket
|
||||
useExecutionEvents({
|
||||
@@ -94,9 +132,22 @@ export function useSidebarRunsList({
|
||||
// Notify parent about counts and loading state
|
||||
useEffect(() => {
|
||||
if (onCountsChange) {
|
||||
onCountsChange({ runsCount, schedulesCount, loading });
|
||||
onCountsChange({
|
||||
runsCount,
|
||||
schedulesCount,
|
||||
templatesCount,
|
||||
triggersCount,
|
||||
loading,
|
||||
});
|
||||
}
|
||||
}, [runsCount, schedulesCount, loading, onCountsChange]);
|
||||
}, [
|
||||
runsCount,
|
||||
schedulesCount,
|
||||
templatesCount,
|
||||
triggersCount,
|
||||
loading,
|
||||
onCountsChange,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (runs.length > 0 && tabValue === "runs" && !activeItem) {
|
||||
@@ -111,15 +162,31 @@ export function useSidebarRunsList({
|
||||
}
|
||||
}, [activeItem, runs.length, schedules, onSelectRun]);
|
||||
|
||||
useEffect(() => {
|
||||
if (templates.length > 0 && tabValue === "templates" && !activeItem) {
|
||||
onSelectRun(templates[0].id, "templates");
|
||||
}
|
||||
}, [templates, activeItem, tabValue, onSelectRun]);
|
||||
|
||||
useEffect(() => {
|
||||
if (triggers.length > 0 && tabValue === "triggers" && !activeItem) {
|
||||
onSelectRun(triggers[0].id, "triggers");
|
||||
}
|
||||
}, [triggers, activeItem, tabValue, onSelectRun]);
|
||||
|
||||
return {
|
||||
runs,
|
||||
schedules,
|
||||
error: schedulesQuery.error || runsQuery.error,
|
||||
templates,
|
||||
triggers,
|
||||
error: schedulesQuery.error || runsQuery.error || presetsQuery.error,
|
||||
loading,
|
||||
runsQuery,
|
||||
tabValue,
|
||||
runsCount,
|
||||
schedulesCount,
|
||||
templatesCount,
|
||||
triggersCount,
|
||||
fetchMoreRuns: runsQuery.fetchNextPage,
|
||||
hasMoreRuns: runsQuery.hasNextPage,
|
||||
isFetchingMoreRuns: runsQuery.isFetchingNextPage,
|
||||
|
||||
@@ -5,8 +5,15 @@ import { useParams } from "next/navigation";
|
||||
import { parseAsString, useQueryStates } from "nuqs";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
|
||||
function parseTab(value: string | null): "runs" | "scheduled" | "templates" {
|
||||
if (value === "runs" || value === "scheduled" || value === "templates") {
|
||||
function parseTab(
|
||||
value: string | null,
|
||||
): "runs" | "scheduled" | "templates" | "triggers" {
|
||||
if (
|
||||
value === "runs" ||
|
||||
value === "scheduled" ||
|
||||
value === "templates" ||
|
||||
value === "triggers"
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
return "runs";
|
||||
@@ -45,6 +52,8 @@ export function useNewAgentLibraryView() {
|
||||
const [sidebarCounts, setSidebarCounts] = useState({
|
||||
runsCount: 0,
|
||||
schedulesCount: 0,
|
||||
templatesCount: 0,
|
||||
triggersCount: 0,
|
||||
});
|
||||
|
||||
const [sidebarLoading, setSidebarLoading] = useState(true);
|
||||
@@ -52,7 +61,9 @@ export function useNewAgentLibraryView() {
|
||||
const hasAnyItems = useMemo(
|
||||
() =>
|
||||
(sidebarCounts.runsCount ?? 0) > 0 ||
|
||||
(sidebarCounts.schedulesCount ?? 0) > 0,
|
||||
(sidebarCounts.schedulesCount ?? 0) > 0 ||
|
||||
(sidebarCounts.templatesCount ?? 0) > 0 ||
|
||||
(sidebarCounts.triggersCount ?? 0) > 0,
|
||||
[sidebarCounts],
|
||||
);
|
||||
|
||||
@@ -65,7 +76,22 @@ export function useNewAgentLibraryView() {
|
||||
}
|
||||
}, [response]);
|
||||
|
||||
function handleSelectRun(id: string, tab?: "runs" | "scheduled") {
|
||||
useEffect(() => {
|
||||
if (
|
||||
activeTab === "triggers" &&
|
||||
sidebarCounts.triggersCount === 0 &&
|
||||
!sidebarLoading
|
||||
) {
|
||||
setQueryStates({
|
||||
activeTab: "runs",
|
||||
});
|
||||
}
|
||||
}, [activeTab, sidebarCounts.triggersCount, sidebarLoading, setQueryStates]);
|
||||
|
||||
function handleSelectRun(
|
||||
id: string,
|
||||
tab?: "runs" | "scheduled" | "templates" | "triggers",
|
||||
) {
|
||||
setQueryStates({
|
||||
activeItem: id,
|
||||
activeTab: tab ?? "runs",
|
||||
@@ -78,7 +104,9 @@ export function useNewAgentLibraryView() {
|
||||
});
|
||||
}
|
||||
|
||||
function handleSetActiveTab(tab: "runs" | "scheduled" | "templates") {
|
||||
function handleSetActiveTab(
|
||||
tab: "runs" | "scheduled" | "templates" | "triggers",
|
||||
) {
|
||||
setQueryStates({
|
||||
activeTab: tab,
|
||||
});
|
||||
@@ -88,11 +116,15 @@ export function useNewAgentLibraryView() {
|
||||
(counts: {
|
||||
runsCount: number;
|
||||
schedulesCount: number;
|
||||
templatesCount: number;
|
||||
triggersCount: number;
|
||||
loading?: boolean;
|
||||
}) => {
|
||||
setSidebarCounts({
|
||||
runsCount: counts.runsCount,
|
||||
schedulesCount: counts.schedulesCount,
|
||||
templatesCount: counts.templatesCount,
|
||||
triggersCount: counts.triggersCount,
|
||||
});
|
||||
if (counts.loading !== undefined) {
|
||||
setSidebarLoading(counts.loading);
|
||||
|
||||
Reference in New Issue
Block a user