create new mission validation and action

This commit is contained in:
Eng. Elias
2024-02-29 14:30:48 +04:00
parent 7dec195a78
commit 61fabd883f
10 changed files with 112 additions and 38 deletions

View File

@@ -46,7 +46,7 @@ const AgentsPage = () => {
<NewAgentModal
showModal={showNewAgentModal}
setShowModal={setShowNewAgentModal}
onAddNewAdent={() => {
onAddNewAgent={() => {
refetch();
}}
/>

View File

@@ -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,

View File

@@ -31,7 +31,7 @@ const typeDefs = `#graphql
type Task {
name: String!
description: String!
agent: Agent!
agent: Agent
}
input TaskInput {

View File

@@ -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">

View File

@@ -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>

View File

@@ -160,6 +160,7 @@ export default function MissionModal(props: {
<div>
<MissionTaskEditor
mission={mission!}
agents={mission?.crew!}
onMissionChange={(mission: Mission) => {
setTempMission(mission);
}}

View File

@@ -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: "",

View File

@@ -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>

View File

@@ -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>

View File

@@ -7,7 +7,7 @@ type Tool =
| "YUOUTUBE_SEARCH";
export type Agent = {
id?: number;
id?: number | string;
role: string;
goal: string;
backstory?: string | null;