create new mission validation and action
This commit is contained in:
@@ -46,7 +46,7 @@ const AgentsPage = () => {
|
||||
<NewAgentModal
|
||||
showModal={showNewAgentModal}
|
||||
setShowModal={setShowNewAgentModal}
|
||||
onAddNewAdent={() => {
|
||||
onAddNewAgent={() => {
|
||||
refetch();
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -54,12 +54,18 @@ const resolvers = {
|
||||
in: body.crew,
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
const tasks = [];
|
||||
for (let task of body.tasks) {
|
||||
const agent = await prisma.agent.findFirst({
|
||||
where: { id: task.agent },
|
||||
});
|
||||
let agent = null;
|
||||
if (task.agent) {
|
||||
agent = await prisma.agent.findFirst({
|
||||
where: { id: task.agent },
|
||||
});
|
||||
}
|
||||
tasks.push({
|
||||
...task,
|
||||
agent,
|
||||
@@ -70,12 +76,11 @@ const resolvers = {
|
||||
name,
|
||||
verbose: !!verbose,
|
||||
process: process ?? Process.SEQUENTIAL,
|
||||
crew: { create: crew },
|
||||
crew: { connect: crew },
|
||||
tasks,
|
||||
result: "",
|
||||
},
|
||||
});
|
||||
mission.id;
|
||||
return mission;
|
||||
},
|
||||
updateMission: async (parent, body, context, info) => {
|
||||
@@ -90,9 +95,12 @@ const resolvers = {
|
||||
const tasks = [];
|
||||
if (body.tasks) {
|
||||
for (let task of body.tasks) {
|
||||
const agent = await prisma.agent.findFirst({
|
||||
where: { id: task.agent },
|
||||
});
|
||||
let agent = null;
|
||||
if (task.agent) {
|
||||
agent = await prisma.agent.findFirst({
|
||||
where: { id: task.agent },
|
||||
});
|
||||
}
|
||||
tasks.push({
|
||||
...task,
|
||||
agent,
|
||||
|
||||
@@ -31,7 +31,7 @@ const typeDefs = `#graphql
|
||||
type Task {
|
||||
name: String!
|
||||
description: String!
|
||||
agent: Agent!
|
||||
agent: Agent
|
||||
}
|
||||
|
||||
input TaskInput {
|
||||
|
||||
@@ -16,7 +16,7 @@ const MissionsPage = () => {
|
||||
|
||||
const [selectedMission, setSelectedMission] = useState<Mission | null>(null);
|
||||
|
||||
const { loading, error, data } = useQuery(GET_MISSIONS);
|
||||
const { loading, error, data, refetch } = useQuery(GET_MISSIONS);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -47,6 +47,9 @@ const MissionsPage = () => {
|
||||
<NewMissionModal
|
||||
showModal={showNewMissionModal}
|
||||
setShowModal={setShowNewMissionModal}
|
||||
onAddNewMission={() => {
|
||||
refetch();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="container m-auto flex flex-wrap flex-col md:flex-row items-center justify-start p-2">
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
"use client";
|
||||
|
||||
import { selectTheme } from "@/data/consts";
|
||||
import { Agent } from "@/types/agent";
|
||||
import { Mission } from "@/types/mission";
|
||||
import { Task } from "@/types/task";
|
||||
import { Button } from "@material-tailwind/react";
|
||||
import React, { useState } from "react";
|
||||
import { TESelect } from "tw-elements-react";
|
||||
|
||||
interface MissionTaskEditorProps {
|
||||
mission: Mission;
|
||||
agents: Array<Agent>;
|
||||
onMissionChange: (updatedMission: Mission) => void;
|
||||
}
|
||||
|
||||
const MissionTaskEditor: React.FC<MissionTaskEditorProps> = ({
|
||||
mission,
|
||||
agents = [],
|
||||
onMissionChange,
|
||||
}) => {
|
||||
const [newTaskName, setNewTaskName] = useState("");
|
||||
const [newTaskAgent, setNewTaskAgent] = useState("");
|
||||
const [newTaskAgent, setNewTaskAgent] = useState<Agent | null>(null);
|
||||
const [newTaskDescription, setNewTaskDescription] = useState("");
|
||||
|
||||
const handleAddTask = () => {
|
||||
const newTask: Task = {
|
||||
name: newTaskName,
|
||||
description: newTaskDescription,
|
||||
agent: null,
|
||||
agent: newTaskAgent,
|
||||
};
|
||||
const updatedTasks = [...(mission?.tasks ?? []), newTask];
|
||||
const updatedMission: Mission = { ...mission, tasks: updatedTasks };
|
||||
@@ -57,7 +61,7 @@ const MissionTaskEditor: React.FC<MissionTaskEditorProps> = ({
|
||||
<div className="ml-3">
|
||||
<strong>Agent: </strong>
|
||||
<span className="bg-gray-200 text-gray-700 rounded-full px-3 py-1 text-sm font-semibold m-1 sm:w-1/2">
|
||||
{task.agent?.role}
|
||||
{task.agent?.role ?? "No Agent"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -92,24 +96,30 @@ const MissionTaskEditor: React.FC<MissionTaskEditorProps> = ({
|
||||
<TESelect
|
||||
className="bg-gray-700 text-white"
|
||||
data={[
|
||||
{ text: "None", value: undefined },
|
||||
{ text: "None", value: -1 },
|
||||
...(mission?.crew ?? []).map((agent) => ({
|
||||
text: agent.role,
|
||||
value: agent.id,
|
||||
})),
|
||||
]}
|
||||
onValueChange={(event: any) => {
|
||||
setNewTaskAgent(event.value);
|
||||
if (event.value) {
|
||||
const agent =
|
||||
agents.find((a) => a.id === event.value) ?? null;
|
||||
setNewTaskAgent(agent);
|
||||
}
|
||||
}}
|
||||
theme={selectTheme}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
<Button
|
||||
color="green"
|
||||
disabled={!newTaskName || !newTaskDescription}
|
||||
onClick={handleAddTask}
|
||||
className="bg-success-600 text-white rounded px-4 py-1 ml-2"
|
||||
placeholder={undefined}
|
||||
>
|
||||
Add Task
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -160,6 +160,7 @@ export default function MissionModal(props: {
|
||||
<div>
|
||||
<MissionTaskEditor
|
||||
mission={mission!}
|
||||
agents={mission?.crew!}
|
||||
onMissionChange={(mission: Mission) => {
|
||||
setTempMission(mission);
|
||||
}}
|
||||
|
||||
@@ -21,14 +21,17 @@ import { useMutation } from "@apollo/client";
|
||||
import { CREATE_AGENT } from "@/utils/graphql_queries";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
import { Gothic_A1 } from "next/font/google";
|
||||
|
||||
function NewAgentModal(props: {
|
||||
showModal: boolean;
|
||||
setShowModal: Function;
|
||||
onAddNewAdent: Function;
|
||||
onAddNewAgent: Function;
|
||||
}) {
|
||||
const { showModal, setShowModal, onAddNewAdent = () => {} } = props;
|
||||
const {
|
||||
showModal,
|
||||
setShowModal,
|
||||
onAddNewAgent: onAddNewAdent = () => {},
|
||||
} = props;
|
||||
|
||||
const [tempAgent, setTempAgent] = useState<Agent>({
|
||||
role: "",
|
||||
|
||||
@@ -15,18 +15,20 @@ import {
|
||||
TESelect,
|
||||
} from "tw-elements-react";
|
||||
import MissionTaskEditor from "../inputs/mission_tasks_editor";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { GET_AGENTS } from "@/utils/graphql_queries";
|
||||
import { useMutation, useQuery } from "@apollo/client";
|
||||
import { CREATE_MISSION, GET_AGENTS } from "@/utils/graphql_queries";
|
||||
import { Agent } from "@/types/agent";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
function NewMissionModal(props: {
|
||||
showModal: boolean;
|
||||
setShowModal: Function;
|
||||
onAddNewMission: Function;
|
||||
}): JSX.Element {
|
||||
const { showModal, setShowModal } = props;
|
||||
const { showModal, setShowModal, onAddNewMission = () => {} } = props;
|
||||
|
||||
const [tempMission, setTempMission] = useState<Mission | null>({
|
||||
id: -1,
|
||||
const [tempMission, setTempMission] = useState<Mission>({
|
||||
name: "",
|
||||
crew: [],
|
||||
tasks: [],
|
||||
@@ -37,6 +39,30 @@ function NewMissionModal(props: {
|
||||
|
||||
const { loading, error, data: agentsData } = useQuery(GET_AGENTS);
|
||||
|
||||
const [createMission] = useMutation(CREATE_MISSION);
|
||||
const [createMissionLoading, setCreateMissionLoading] = useState(false);
|
||||
|
||||
const handleCreateMission = (missionData: Mission) => {
|
||||
setCreateMissionLoading(true);
|
||||
return createMission({
|
||||
variables: {
|
||||
...missionData,
|
||||
// TODO: Check why Prisma Schema (id Int) return String.
|
||||
crew: missionData.crew
|
||||
.filter((agent) => Number.parseInt(agent?.id as string))
|
||||
.map((agent) => Number.parseInt(agent.id as string)),
|
||||
tasks: missionData.tasks.map((task) => ({
|
||||
...task,
|
||||
agent: Number.parseInt(task.agent?.id as string),
|
||||
})),
|
||||
},
|
||||
}).finally(() => {
|
||||
setCreateMissionLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const ReactSwal = withReactContent(Swal);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TEModal show={showModal} setShow={setShowModal}>
|
||||
@@ -144,6 +170,7 @@ function NewMissionModal(props: {
|
||||
<div>
|
||||
<MissionTaskEditor
|
||||
mission={tempMission!}
|
||||
agents={agentsData?.agents}
|
||||
onMissionChange={(mission: Mission) => {
|
||||
setTempMission(mission);
|
||||
}}
|
||||
@@ -168,21 +195,43 @@ function NewMissionModal(props: {
|
||||
|
||||
<TEModalFooter>
|
||||
<TERipple rippleColor="light">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-block rounded bg-primary-100 px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-primary-700 transition duration-150 ease-in-out hover:bg-primary-accent-100 focus:bg-primary-accent-100 focus:outline-none focus:ring-0 active:bg-primary-accent-200"
|
||||
<Button
|
||||
color="white"
|
||||
onClick={() => setShowModal(false)}
|
||||
placeholder={undefined}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</Button>
|
||||
</TERipple>
|
||||
<TERipple rippleColor="light">
|
||||
<button
|
||||
type="button"
|
||||
className="ml-1 inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-[0_4px_9px_-4px_#3b71ca] transition duration-150 ease-in-out hover:bg-primary-600 hover:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] focus:bg-primary-600 focus:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] focus:outline-none focus:ring-0 active:bg-primary-700 active:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.3),0_4px_18px_0_rgba(59,113,202,0.2)] dark:shadow-[0_4px_9px_-4px_rgba(59,113,202,0.5)] dark:hover:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)] dark:focus:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)] dark:active:shadow-[0_8px_9px_-4px_rgba(59,113,202,0.2),0_4px_18px_0_rgba(59,113,202,0.1)]"
|
||||
<Button
|
||||
color="green"
|
||||
loading={createMissionLoading}
|
||||
disabled={!tempMission.name || createMissionLoading}
|
||||
onClick={() => {
|
||||
handleCreateMission(tempMission)
|
||||
.then(() => {
|
||||
setShowModal(false);
|
||||
ReactSwal.fire({
|
||||
title: "New Mission",
|
||||
text: "New mission created successfully",
|
||||
icon: "success",
|
||||
});
|
||||
onAddNewMission();
|
||||
})
|
||||
.catch((error) => {
|
||||
ReactSwal.fire({
|
||||
title: "Error",
|
||||
text: error,
|
||||
icon: "error",
|
||||
});
|
||||
});
|
||||
}}
|
||||
className="mx-1"
|
||||
placeholder={undefined}
|
||||
>
|
||||
Save changes
|
||||
</button>
|
||||
Add
|
||||
</Button>
|
||||
</TERipple>
|
||||
</TEModalFooter>
|
||||
</TEModalContent>
|
||||
|
||||
@@ -29,7 +29,7 @@ export function TasksAccordion({ tasks }: { tasks: Array<Task> }) {
|
||||
<AccordionBody className="pt-0 text-base font-normal">
|
||||
<div className="mb-3">
|
||||
<span className="bg-gray-200 text-gray-700 rounded-full px-3 py-1 text-sm font-semibold m-1 sm:w-1/2">
|
||||
{task.agent?.role}
|
||||
{task.agent?.role ?? "No Agent"}
|
||||
</span>
|
||||
</div>
|
||||
<div>{task.description}</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ type Tool =
|
||||
| "YUOUTUBE_SEARCH";
|
||||
|
||||
export type Agent = {
|
||||
id?: number;
|
||||
id?: number | string;
|
||||
role: string;
|
||||
goal: string;
|
||||
backstory?: string | null;
|
||||
|
||||
Reference in New Issue
Block a user