Skip to content

Fixes #146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 6, 2024
Merged

Fixes #146

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 136 additions & 53 deletions src/app/crews/[id]/manage/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
"use client";

import { useState, useEffect } from "react";
import { useState, useEffect, useCallback } from "react";
import { useParams, useRouter } from "next/navigation";
import { supabase } from "@/utils/supabase/client";
import AgentManagement from "@/components/agents/AgentManagement";
import TaskManagement from "@/components/tasks/TaskManagement";
import { Card, CardContent } from "@/components/ui/card";
import { Agent, CrewWithCron, Task } from "@/types/supabase";
import { Switch } from "@/components/ui/switch";
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";

import {
TooltipProvider,
Tooltip,
TooltipTrigger,
TooltipContent,
} from "@/components/ui/tooltip";
import { HelpCircle, Trash2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useToast } from "@/hooks/use-toast";
Expand Down Expand Up @@ -44,16 +50,69 @@ export default function CrewDetails() {
const [editForm, setEditForm] = useState({
name: "",
description: "",
is_public: false
is_public: false,
});
const [cronInput, setCronInput] = useState("");
const [cronEnabled, setCronEnabled] = useState(false);
const [cronId, setCronId] = useState(0);

const fetchAgents = useCallback(async () => {
try {
const { data, error } = await supabase
.from("agents")
.select("*")
.eq("crew_id", id);

if (error) throw error;

setAgents(data || []);
} catch (error) {
console.error("Error fetching agents:", error);
toast({
title: "Error",
description: "Failed to fetch agents.",
variant: "destructive",
});
}
}, [id, toast]);

useEffect(() => {
if (id) {
fetchAgents();
}
}, [id, fetchAgents]);

const fetchTasks = useCallback(async () => {
try {
const { data, error } = await supabase
.from("tasks")
.select("*")
.eq("crew_id", id);

if (error) throw error;

setTasks(data || []);
} catch (error) {
console.error("Error fetching tasks:", error);
toast({
title: "Error",
description: "Failed to fetch tasks.",
variant: "destructive",
});
}
}, [id, toast]);

useEffect(() => {
fetchTasks();
}, [fetchTasks]);

// Fetch current user
useEffect(() => {
async function fetchUser() {
const { data: { user }, error } = await supabase.auth.getUser();
const {
data: { user },
error,
} = await supabase.auth.getUser();
if (error) {
console.error("Error fetching user:", error);
return;
Expand All @@ -69,21 +128,17 @@ export default function CrewDetails() {
useEffect(() => {
async function fetchData() {
try {
const [crewResponse, agentsResponse, tasksResponse] = await Promise.all([
supabase
.from("crews")
.select(`*, crons(id, enabled, input, created_at)`)
.eq("id", id)
.single(),
supabase
.from("agents")
.select("*")
.eq("crew_id", id),
supabase
.from("tasks")
.select("*")
.eq("crew_id", id)
]);
const [crewResponse, agentsResponse, tasksResponse] = await Promise.all(
[
supabase
.from("crews")
.select(`*, crons(id, enabled, input, created_at)`)
.eq("id", id)
.single(),
supabase.from("agents").select("*").eq("crew_id", id),
supabase.from("tasks").select("*").eq("crew_id", id),
]
);

if (crewResponse.error) throw crewResponse.error;
if (agentsResponse.error) throw agentsResponse.error;
Expand All @@ -96,7 +151,7 @@ export default function CrewDetails() {
setEditForm({
name: crewData.name,
description: crewData.description || "",
is_public: crewData.is_public || false
is_public: crewData.is_public || false,
});
if (crewData.crons?.[0]?.input) {
setCronId(crewData.crons[0].id);
Expand All @@ -111,7 +166,7 @@ export default function CrewDetails() {
toast({
title: "Error",
description: "Failed to load crew data. Please refresh the page.",
variant: "destructive"
variant: "destructive",
});
}
}
Expand All @@ -132,7 +187,7 @@ export default function CrewDetails() {
.update({
name: editForm.name,
description: editForm.description,
is_public: editForm.is_public
is_public: editForm.is_public,
})
.eq("id", crew.id);

Expand All @@ -153,22 +208,24 @@ export default function CrewDetails() {
name: editForm.name,
description: editForm.description,
is_public: editForm.is_public,
cron: crew.cron ? {
...crew.cron,
input: cronInput
} : null
cron: crew.cron
? {
...crew.cron,
input: cronInput,
}
: null,
});

toast({
title: "Success",
description: "Crew settings updated successfully."
description: "Crew settings updated successfully.",
});
} catch (error) {
console.error("Error saving settings:", error);
toast({
title: "Error",
description: "Failed to save crew settings. Please try again.",
variant: "destructive"
variant: "destructive",
});
} finally {
setIsSaving(false);
Expand All @@ -187,7 +244,7 @@ export default function CrewDetails() {
crew_id: crew.id,
enabled: true,
profile_id: currentUser,
input: ""
input: "",
})
.select()
.single();
Expand All @@ -201,7 +258,7 @@ export default function CrewDetails() {
setCronEnabled(true);
toast({
title: "Success",
description: "Autonomous running enabled."
description: "Autonomous running enabled.",
});
}
} else if (cronId) {
Expand All @@ -220,15 +277,18 @@ export default function CrewDetails() {

toast({
title: "Success",
description: `Autonomous running ${checked ? "enabled" : "disabled"}.`
description: `Autonomous running ${
checked ? "enabled" : "disabled"
}.`,
});
}
} catch (error) {
console.error("Error toggling autonomous mode:", error);
toast({
title: "Error",
description: "Failed to update autonomous running status. Please try again.",
variant: "destructive"
description:
"Failed to update autonomous running status. Please try again.",
variant: "destructive",
});
}
};
Expand All @@ -250,7 +310,7 @@ export default function CrewDetails() {

toast({
title: "Success",
description: "Crew deleted successfully"
description: "Crew deleted successfully",
});

router.push("/crews");
Expand All @@ -259,15 +319,19 @@ export default function CrewDetails() {
toast({
title: "Error",
description: "Failed to delete crew. Please try again.",
variant: "destructive"
variant: "destructive",
});
} finally {
setIsDeleting(false);
}
};

if (!crew) {
return <div className="flex justify-center items-center min-h-[200px]">Loading...</div>;
return (
<div className="flex justify-center items-center min-h-[200px]">
Loading...
</div>
);
}

return (
Expand All @@ -282,15 +346,24 @@ export default function CrewDetails() {
<input
type="text"
value={editForm.name}
onChange={(e) => setEditForm(prev => ({ ...prev, name: e.target.value }))}
onChange={(e) =>
setEditForm((prev) => ({ ...prev, name: e.target.value }))
}
className="w-full px-3 py-2 border rounded-md"
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">Description</label>
<label className="block text-sm font-medium mb-1">
Description
</label>
<textarea
value={editForm.description}
onChange={(e) => setEditForm(prev => ({ ...prev, description: e.target.value }))}
onChange={(e) =>
setEditForm((prev) => ({
...prev,
description: e.target.value,
}))
}
className="w-full px-3 py-2 border rounded-md"
rows={3}
/>
Expand All @@ -302,7 +375,9 @@ export default function CrewDetails() {
</div>
<Switch
checked={editForm.is_public}
onCheckedChange={(checked) => setEditForm(prev => ({ ...prev, is_public: checked }))}
onCheckedChange={(checked) =>
setEditForm((prev) => ({ ...prev, is_public: checked }))
}
/>
</div>
<div className="flex items-center">
Expand All @@ -318,7 +393,8 @@ export default function CrewDetails() {
<TooltipContent className="max-w-sm p-4">
<p className="font-medium mb-2">Cron Job</p>
<p className="text-sm">
Enable to run this crew automatically on an hourly schedule.
Enable to run this crew automatically on an hourly
schedule.
</p>
</TooltipContent>
</Tooltip>
Expand All @@ -332,7 +408,9 @@ export default function CrewDetails() {
</div>
{cronEnabled && (
<div className="pt-2">
<label className="block text-sm font-medium mb-1">Cron Input Prompt</label>
<label className="block text-sm font-medium mb-1">
Cron Input Prompt
</label>
<div className="flex gap-2">
<input
type="text"
Expand All @@ -351,10 +429,13 @@ export default function CrewDetails() {
<TooltipContent className="max-w-sm p-4">
<p className="font-medium mb-2">Example Prompt</p>
<p className="text-sm">
Check Bitcoin price and if its above $40,000, analyze market sentiment from the last hour and provide a summary of bullish/bearish indicators
Check Bitcoin price and if its above $40,000,
analyze market sentiment from the last hour and
provide a summary of bullish/bearish indicators
</p>
<p className="text-xs mt-2 text-muted-foreground">
Note: This job will run every hour. The schedule is fixed and cannot be modified.
Note: This job will run every hour. The schedule is
fixed and cannot be modified.
</p>
</TooltipContent>
</Tooltip>
Expand All @@ -363,10 +444,7 @@ export default function CrewDetails() {
</div>
)}
<div className="pt-4 flex justify-start gap-2">
<Button
onClick={handleSaveSettings}
disabled={isSaving}
>
<Button onClick={handleSaveSettings} disabled={isSaving}>
{isSaving ? "Saving..." : "Save Changes"}
</Button>
<AlertDialog>
Expand All @@ -380,8 +458,9 @@ export default function CrewDetails() {
<AlertDialogHeader>
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete the crew
and all its associated data including agents, tasks, and jobs.
This action cannot be undone. This will permanently
delete the crew and all its associated data including
agents, tasks, and jobs.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
Expand All @@ -405,7 +484,9 @@ export default function CrewDetails() {
<CardContent className="pt-6">
<AgentManagement
crewId={crew.id}
onAgentAdded={() => { }}
onAgentAdded={() => {
fetchAgents();
}}
/>
</CardContent>
</Card>
Expand All @@ -416,8 +497,10 @@ export default function CrewDetails() {
tasks={tasks}
agents={agents}
crewId={crew.id}
onTaskAdded={() => { }}
onEditTask={() => { }}
onTaskAdded={() => {
fetchTasks();
}}
onEditTask={() => {}}
currentUser={currentUser}
/>
</CardContent>
Expand Down
2 changes: 1 addition & 1 deletion src/components/agents/AgentManagement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export default function AgentManagement({
}}
>
<DialogTrigger asChild>
<Button >
<Button>
<PlusIcon className="mr-2 h-4 w-4" />
Add Agent
</Button>
Expand Down
Loading
Loading