Files
mission-control/frontend/src/app/projects/page.tsx
2026-02-02 01:56:12 +05:30

92 lines
3.0 KiB
TypeScript

"use client";
import { useState } from "react";
import Link from "next/link";
import styles from "@/app/_components/Shell.module.css";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { normalizeProjects } from "@/lib/normalize";
import {
useCreateProjectProjectsPost,
useListProjectsProjectsGet,
} from "@/api/generated/projects/projects";
export default function ProjectsPage() {
const [name, setName] = useState("");
const projects = useListProjectsProjectsGet();
const projectList = normalizeProjects(projects.data);
const createProject = useCreateProjectProjectsPost({
mutation: {
onSuccess: () => {
setName("");
projects.refetch();
},
},
});
const sorted = projectList.slice().sort((a, b) => a.name.localeCompare(b.name));
return (
<main>
<div className={styles.topbar}>
<div>
<h1 className={styles.h1}>Projects</h1>
<p className={styles.p}>Create, view, and drill into projects.</p>
</div>
<Button variant="outline" onClick={() => projects.refetch()} disabled={projects.isFetching}>
Refresh
</Button>
</div>
<div className={styles.grid2}>
<div className={styles.card}>
<div className={styles.cardTitle}>Create project</div>
<div className={styles.list}>
<Input
placeholder="Project name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<Button
onClick={() => createProject.mutate({ data: { name, status: "active" } })}
disabled={!name.trim() || createProject.isPending}
>
Create
</Button>
{createProject.error ? (
<div className={styles.mono}>{(createProject.error as Error).message}</div>
) : null}
</div>
</div>
<Card>
<CardHeader>
<CardTitle>All projects</CardTitle>
<CardDescription>{sorted.length} total</CardDescription>
</CardHeader>
<CardContent>
<div className={styles.list}>
{sorted.map((p) => (
<div key={p.id ?? p.name} className={styles.item}>
<div style={{ fontWeight: 600 }}>{p.name}</div>
<div className={styles.mono} style={{ display: "flex", gap: 10, alignItems: "center" }}>
<span>{p.status}</span>
{p.id ? (
<Link href={`/projects/${p.id}`} className={styles.badge}>Open</Link>
) : null}
</div>
</div>
))}
{sorted.length === 0 ? <div className={styles.mono}>No projects yet.</div> : null}
</div>
</CardContent>
</Card>
</div>
</main>
);
}