|
1 | 1 | import { useState, useEffect, FormEvent } from 'react'; |
2 | | -import { api, Project } from '../api/client'; |
| 2 | +import { Link } from 'react-router-dom'; |
| 3 | +import { api, Project, Deployment, APIError } from '../api/client'; |
3 | 4 | import { Navigation } from '../components/Navigation'; |
4 | | -import { APIError } from '../api/client'; |
| 5 | + |
| 6 | +function ProjectCard({ project }: { project: Project }) { |
| 7 | + const [latestDeployment, setLatestDeployment] = useState<Deployment | null>(null); |
| 8 | + const [loading, setLoading] = useState(true); |
| 9 | + |
| 10 | + useEffect(() => { |
| 11 | + loadLatestDeployment(); |
| 12 | + }, [project.id]); |
| 13 | + |
| 14 | + const loadLatestDeployment = async () => { |
| 15 | + try { |
| 16 | + const deployments = await api.projects.listDeployments(project.id); |
| 17 | + if (deployments.length > 0) { |
| 18 | + setLatestDeployment(deployments[0]); |
| 19 | + } |
| 20 | + } catch (err) { |
| 21 | + console.error('Failed to load deployments', err); |
| 22 | + } finally { |
| 23 | + setLoading(false); |
| 24 | + } |
| 25 | + }; |
| 26 | + |
| 27 | + return ( |
| 28 | + <Link to={`/projects/${project.id}`} className="project-card-link"> |
| 29 | + <div className="project-card clickable"> |
| 30 | + <div className="project-card-header"> |
| 31 | + <h3>{project.name}</h3> |
| 32 | + <span className={`workload-badge ${project.workload_type}`}> |
| 33 | + {project.workload_type === 'web_service' ? 'Web Service' : 'Static Site'} |
| 34 | + </span> |
| 35 | + </div> |
| 36 | + |
| 37 | + <div className="project-details"> |
| 38 | + <p><strong>Repository:</strong> {project.git_repo_url}</p> |
| 39 | + <p><strong>Branch:</strong> {project.branch}</p> |
| 40 | + </div> |
| 41 | + |
| 42 | + <div className="deployment-summary"> |
| 43 | + {loading ? ( |
| 44 | + <p className="status-text">Loading...</p> |
| 45 | + ) : latestDeployment ? ( |
| 46 | + <div className="status-line"> |
| 47 | + <span className={`status-badge ${latestDeployment.status}`}> |
| 48 | + {latestDeployment.status} |
| 49 | + </span> |
| 50 | + {latestDeployment.status === 'running' && <span className="live-indicator">● Live</span>} |
| 51 | + </div> |
| 52 | + ) : ( |
| 53 | + <p className="status-text">No deployments</p> |
| 54 | + )} |
| 55 | + </div> |
| 56 | + </div> |
| 57 | + </Link> |
| 58 | + ); |
| 59 | +} |
5 | 60 |
|
6 | 61 | export function DashboardPage() { |
7 | 62 | const [projects, setProjects] = useState<Project[]>([]); |
@@ -50,7 +105,7 @@ export function DashboardPage() { |
50 | 105 | formData.branch, |
51 | 106 | formData.workloadType |
52 | 107 | ); |
53 | | - setProjects([...projects, newProject]); |
| 108 | + setProjects([newProject, ...projects]); |
54 | 109 | setShowCreateForm(false); |
55 | 110 | setFormData({ name: '', gitRepoUrl: '', branch: 'main', workloadType: 'web_service' }); |
56 | 111 | } catch (err) { |
@@ -174,27 +229,7 @@ export function DashboardPage() { |
174 | 229 | ) : ( |
175 | 230 | <div className="projects-list"> |
176 | 231 | {projects.map((project) => ( |
177 | | - <div key={project.id} className="project-card"> |
178 | | - <div className="project-card-header"> |
179 | | - <h3>{project.name}</h3> |
180 | | - <span className={`workload-badge ${project.workload_type}`}> |
181 | | - {project.workload_type === 'web_service' |
182 | | - ? 'Web Service' |
183 | | - : 'Static Site'} |
184 | | - </span> |
185 | | - </div> |
186 | | - <div className="project-details"> |
187 | | - <p> |
188 | | - <strong>Repository:</strong> {project.git_repo_url} |
189 | | - </p> |
190 | | - <p> |
191 | | - <strong>Branch:</strong> {project.branch} |
192 | | - </p> |
193 | | - <p className="project-date"> |
194 | | - Created {new Date(project.created_at).toLocaleDateString()} |
195 | | - </p> |
196 | | - </div> |
197 | | - </div> |
| 232 | + <ProjectCard key={project.id} project={project} /> |
198 | 233 | ))} |
199 | 234 | </div> |
200 | 235 | )} |
|
0 commit comments