Fixes
This commit is contained in:
@@ -40,16 +40,6 @@ export function Navbar() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-4">
|
|
||||||
{!isHome && (
|
|
||||||
<Button asChild variant="ghost">
|
|
||||||
<Link to="/">
|
|
||||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
|
||||||
Home
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,80 +1,100 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from "react";
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from "react-router-dom";
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
import {
|
||||||
import { Badge } from '@/components/ui/badge'
|
Card,
|
||||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
CardContent,
|
||||||
import { Project, ApiResponse } from 'shared/types'
|
CardDescription,
|
||||||
import { ProjectForm } from './project-form'
|
CardHeader,
|
||||||
import { makeRequest } from '@/lib/api'
|
CardTitle,
|
||||||
import { Plus, Edit, Trash2, Calendar, AlertCircle, Loader2, MoreHorizontal, ExternalLink } from 'lucide-react'
|
} from "@/components/ui/card";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
|
import { Project, ApiResponse } from "shared/types";
|
||||||
|
import { ProjectForm } from "./project-form";
|
||||||
|
import { makeRequest } from "@/lib/api";
|
||||||
|
import {
|
||||||
|
Plus,
|
||||||
|
Edit,
|
||||||
|
Trash2,
|
||||||
|
Calendar,
|
||||||
|
AlertCircle,
|
||||||
|
Loader2,
|
||||||
|
MoreHorizontal,
|
||||||
|
ExternalLink,
|
||||||
|
} from "lucide-react";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu'
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
|
||||||
export function ProjectList() {
|
export function ProjectList() {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate();
|
||||||
const [projects, setProjects] = useState<Project[]>([])
|
const [projects, setProjects] = useState<Project[]>([]);
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false);
|
||||||
const [showForm, setShowForm] = useState(false)
|
const [showForm, setShowForm] = useState(false);
|
||||||
const [editingProject, setEditingProject] = useState<Project | null>(null)
|
const [editingProject, setEditingProject] = useState<Project | null>(null);
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
const fetchProjects = async () => {
|
const fetchProjects = async () => {
|
||||||
setLoading(true)
|
setLoading(true);
|
||||||
setError('')
|
setError("");
|
||||||
try {
|
try {
|
||||||
const response = await makeRequest('/api/projects')
|
const response = await makeRequest("/api/projects");
|
||||||
const data: ApiResponse<Project[]> = await response.json()
|
const data: ApiResponse<Project[]> = await response.json();
|
||||||
if (data.success && data.data) {
|
if (data.success && data.data) {
|
||||||
setProjects(data.data)
|
setProjects(data.data);
|
||||||
} else {
|
} else {
|
||||||
setError('Failed to load projects')
|
setError("Failed to load projects");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch projects:', error)
|
console.error("Failed to fetch projects:", error);
|
||||||
setError('Failed to connect to server')
|
setError("Failed to connect to server");
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleDelete = async (id: string, name: string) => {
|
const handleDelete = async (id: string, name: string) => {
|
||||||
if (!confirm(`Are you sure you want to delete "${name}"? This action cannot be undone.`)) return
|
if (
|
||||||
|
!confirm(
|
||||||
|
`Are you sure you want to delete "${name}"? This action cannot be undone.`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await makeRequest(`/api/projects/${id}`, {
|
const response = await makeRequest(`/api/projects/${id}`, {
|
||||||
method: 'DELETE',
|
method: "DELETE",
|
||||||
})
|
});
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
fetchProjects()
|
fetchProjects();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to delete project:', error)
|
console.error("Failed to delete project:", error);
|
||||||
setError('Failed to delete project')
|
setError("Failed to delete project");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleEdit = (project: Project) => {
|
const handleEdit = (project: Project) => {
|
||||||
setEditingProject(project)
|
setEditingProject(project);
|
||||||
setShowForm(true)
|
setShowForm(true);
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleFormSuccess = () => {
|
const handleFormSuccess = () => {
|
||||||
setShowForm(false)
|
setShowForm(false);
|
||||||
setEditingProject(null)
|
setEditingProject(null);
|
||||||
fetchProjects()
|
fetchProjects();
|
||||||
}
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchProjects()
|
fetchProjects();
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6 p-8">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold tracking-tight">Projects</h1>
|
<h1 className="text-3xl font-bold tracking-tight">Projects</h1>
|
||||||
@@ -91,9 +111,7 @@ export function ProjectList() {
|
|||||||
{error && (
|
{error && (
|
||||||
<Alert variant="destructive">
|
<Alert variant="destructive">
|
||||||
<AlertCircle className="h-4 w-4" />
|
<AlertCircle className="h-4 w-4" />
|
||||||
<AlertDescription>
|
<AlertDescription>{error}</AlertDescription>
|
||||||
{error}
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -112,10 +130,7 @@ export function ProjectList() {
|
|||||||
<p className="mt-2 text-sm text-muted-foreground">
|
<p className="mt-2 text-sm text-muted-foreground">
|
||||||
Get started by creating your first project.
|
Get started by creating your first project.
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button className="mt-4" onClick={() => setShowForm(true)}>
|
||||||
className="mt-4"
|
|
||||||
onClick={() => setShowForm(true)}
|
|
||||||
>
|
|
||||||
<Plus className="mr-2 h-4 w-4" />
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
Create your first project
|
Create your first project
|
||||||
</Button>
|
</Button>
|
||||||
@@ -124,22 +139,21 @@ export function ProjectList() {
|
|||||||
) : (
|
) : (
|
||||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||||
{projects.map((project) => (
|
{projects.map((project) => (
|
||||||
<Card
|
<Card
|
||||||
key={project.id}
|
key={project.id}
|
||||||
className="hover:shadow-md transition-shadow cursor-pointer"
|
className="hover:shadow-md transition-shadow cursor-pointer"
|
||||||
onClick={() => navigate(`/projects/${project.id}/tasks`)}
|
onClick={() => navigate(`/projects/${project.id}/tasks`)}
|
||||||
>
|
>
|
||||||
<CardHeader className="pb-3">
|
<CardHeader className="pb-3">
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<CardTitle className="text-lg">
|
<CardTitle className="text-lg">{project.name}</CardTitle>
|
||||||
{project.name}
|
|
||||||
</CardTitle>
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Badge variant="secondary">
|
<Badge variant="secondary">Active</Badge>
|
||||||
Active
|
|
||||||
</Badge>
|
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild onClick={(e) => e.stopPropagation()}>
|
<DropdownMenuTrigger
|
||||||
|
asChild
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -149,24 +163,28 @@ export function ProjectList() {
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem onClick={(e) => {
|
<DropdownMenuItem
|
||||||
e.stopPropagation()
|
onClick={(e) => {
|
||||||
navigate(`/projects/${project.id}`)
|
e.stopPropagation();
|
||||||
}}>
|
navigate(`/projects/${project.id}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ExternalLink className="mr-2 h-4 w-4" />
|
<ExternalLink className="mr-2 h-4 w-4" />
|
||||||
View Project
|
View Project
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={(e) => {
|
<DropdownMenuItem
|
||||||
e.stopPropagation()
|
onClick={(e) => {
|
||||||
handleEdit(project)
|
e.stopPropagation();
|
||||||
}}>
|
handleEdit(project);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Edit className="mr-2 h-4 w-4" />
|
<Edit className="mr-2 h-4 w-4" />
|
||||||
Edit
|
Edit
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation();
|
||||||
handleDelete(project.id, project.name)
|
handleDelete(project.id, project.name);
|
||||||
}}
|
}}
|
||||||
className="text-destructive"
|
className="text-destructive"
|
||||||
>
|
>
|
||||||
@@ -190,12 +208,12 @@ export function ProjectList() {
|
|||||||
<ProjectForm
|
<ProjectForm
|
||||||
open={showForm}
|
open={showForm}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setShowForm(false)
|
setShowForm(false);
|
||||||
setEditingProject(null)
|
setEditingProject(null);
|
||||||
}}
|
}}
|
||||||
onSuccess={handleFormSuccess}
|
onSuccess={handleFormSuccess}
|
||||||
project={editingProject}
|
project={editingProject}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,6 +203,11 @@ export function TaskDetailsPanel({
|
|||||||
);
|
);
|
||||||
setSelectedAttempt(latestAttempt);
|
setSelectedAttempt(latestAttempt);
|
||||||
fetchAttemptActivities(latestAttempt.id);
|
fetchAttemptActivities(latestAttempt.id);
|
||||||
|
} else {
|
||||||
|
// Clear state when no attempts exist
|
||||||
|
setSelectedAttempt(null);
|
||||||
|
setAttemptActivities([]);
|
||||||
|
setExecutionProcesses({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -477,7 +482,8 @@ export function TaskDetailsPanel({
|
|||||||
onClick={() => createNewAttempt()}
|
onClick={() => createNewAttempt()}
|
||||||
className="rounded-r-none border-r-0"
|
className="rounded-r-none border-r-0"
|
||||||
>
|
>
|
||||||
Attempt with{" "}
|
{selectedAttempt ? "Retry " : "Attempt "}
|
||||||
|
with{" "}
|
||||||
{
|
{
|
||||||
availableExecutors.find(
|
availableExecutors.find(
|
||||||
(e) => e.id === selectedExecutor
|
(e) => e.id === selectedExecutor
|
||||||
|
|||||||
Reference in New Issue
Block a user