diff --git a/src/components/CreateProjectModal.tsx b/src/components/CreateProjectModal.tsx
new file mode 100644
index 0000000..0d8a696
--- /dev/null
+++ b/src/components/CreateProjectModal.tsx
@@ -0,0 +1,108 @@
+import React, { useState } from 'react'
+import apiClient from '../api/client'
+
+interface CreateProjectModalProps {
+ isOpen: boolean
+ onClose: () => void
+ onCreated: () => void
+}
+
+export default function CreateProjectModal({ isOpen, onClose, onCreated }: CreateProjectModalProps) {
+ const [formData, setFormData] = useState({
+ name: '',
+ description: '',
+ giteaOwner: 'admin',
+ giteaRepoName: '',
+ })
+ const [loading, setLoading] = useState(false)
+ const [error, setError] = useState('')
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+ setLoading(true)
+ setError('')
+
+ try {
+ await apiClient.post('/projects', formData)
+ onCreated()
+ onClose()
+ setFormData({ name: '', description: '', giteaOwner: 'admin', giteaRepoName: '' })
+ } catch (err: any) {
+ setError(err.response?.data?.message || 'Failed to create project')
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ if (!isOpen) return null
+
+ return (
+
+
+
Create New Project
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+
+
+ )
+}
diff --git a/src/components/CreateTaskModal.tsx b/src/components/CreateTaskModal.tsx
new file mode 100644
index 0000000..508a77e
--- /dev/null
+++ b/src/components/CreateTaskModal.tsx
@@ -0,0 +1,127 @@
+import React, { useState } from 'react'
+import apiClient from '../api/client'
+import type { Project } from '../types'
+
+interface CreateTaskModalProps {
+ isOpen: boolean
+ onClose: () => void
+ onCreated: () => void
+ projects: Project[]
+}
+
+export default function CreateTaskModal({ isOpen, onClose, onCreated, projects }: CreateTaskModalProps) {
+ const [formData, setFormData] = useState({
+ projectId: '',
+ title: '',
+ description: '',
+ priority: 'medium',
+ })
+ const [loading, setLoading] = useState(false)
+ const [error, setError] = useState('')
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+ setLoading(true)
+ setError('')
+
+ try {
+ await apiClient.post('/tasks', formData)
+ onCreated()
+ onClose()
+ setFormData({ projectId: '', title: '', description: '', priority: 'medium' })
+ } catch (err: any) {
+ setError(err.response?.data?.message || 'Failed to create task')
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ if (!isOpen) return null
+
+ return (
+
+
+
Create New Task
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+
+
+ )
+}
diff --git a/src/components/StatsCards.tsx b/src/components/StatsCards.tsx
new file mode 100644
index 0000000..9ba73f9
--- /dev/null
+++ b/src/components/StatsCards.tsx
@@ -0,0 +1,71 @@
+import React from 'react'
+import type { Project, Task, Agent } from '../types'
+
+interface StatsCardsProps {
+ projects: Project[]
+ tasks: Task[]
+ agents: Agent[]
+}
+
+export default function StatsCards({ projects, tasks, agents }: StatsCardsProps) {
+ const activeProjects = projects.filter((p) => p.status === 'active').length
+ const activeTasks = tasks.filter((t) => ['backlog', 'in_progress', 'needs_input', 'ready_to_test'].includes(t.state)).length
+ const idleAgents = agents.filter((a) => a.status === 'idle').length
+ const busyAgents = agents.filter((a) => a.status === 'busy').length
+
+ const stats = [
+ {
+ name: 'Active Projects',
+ value: activeProjects,
+ total: projects.length,
+ color: 'blue',
+ },
+ {
+ name: 'Active Tasks',
+ value: activeTasks,
+ total: tasks.length,
+ color: 'purple',
+ },
+ {
+ name: 'Idle Agents',
+ value: idleAgents,
+ total: agents.length,
+ color: 'green',
+ },
+ {
+ name: 'Busy Agents',
+ value: busyAgents,
+ total: agents.length,
+ color: 'orange',
+ },
+ ]
+
+ const getColorClasses = (color: string) => {
+ switch (color) {
+ case 'blue':
+ return 'bg-blue-50 text-blue-700 border-blue-200'
+ case 'purple':
+ return 'bg-purple-50 text-purple-700 border-purple-200'
+ case 'green':
+ return 'bg-green-50 text-green-700 border-green-200'
+ case 'orange':
+ return 'bg-orange-50 text-orange-700 border-orange-200'
+ default:
+ return 'bg-gray-50 text-gray-700 border-gray-200'
+ }
+ }
+
+ return (
+
+ {stats.map((stat) => (
+
+
{stat.name}
+
+ {stat.value}
+ /{stat.total}
+
+
+ ))}
+
+ )
+}
diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx
index 0b78224..be6a15e 100644
--- a/src/pages/Dashboard.tsx
+++ b/src/pages/Dashboard.tsx
@@ -4,6 +4,9 @@ import type { Project, Task, Agent } from '../types'
import ProjectList from '../components/ProjectList'
import TaskList from '../components/TaskList'
import AgentStatus from '../components/AgentStatus'
+import StatsCards from '../components/StatsCards'
+import CreateProjectModal from '../components/CreateProjectModal'
+import CreateTaskModal from '../components/CreateTaskModal'
export default function Dashboard() {
const [projects, setProjects] = useState([])
@@ -11,6 +14,8 @@ export default function Dashboard() {
const [agents, setAgents] = useState([])
const [loading, setLoading] = useState(true)
const [activeTab, setActiveTab] = useState<'projects' | 'tasks' | 'agents'>('projects')
+ const [showProjectModal, setShowProjectModal] = useState(false)
+ const [showTaskModal, setShowTaskModal] = useState(false)
useEffect(() => {
loadData()
@@ -47,11 +52,29 @@ export default function Dashboard() {
return (
-
-
AiWorker Dashboard
-
Manage your AI agents and development tasks
+
+
+
AiWorker Dashboard
+
Manage your AI agents and development tasks
+
+
+
+
+
+
+
{/* Tabs */}
+
+ {/* Modals */}
+
setShowProjectModal(false)}
+ onCreated={loadData}
+ />
+ setShowTaskModal(false)}
+ onCreated={loadData}
+ projects={projects}
+ />
)
}