import { createFileRoute, Link } from "@tanstack/react-router"; import { useState } from "react"; import { z } from "zod"; import { deleteMilestone, listMilestones, saveMilestone } from "~/fns/milestones"; import { useAction } from "~/hooks/use-action"; import { ProgressBar } from "~/components/ProgressBar"; import { EmptyState } from "~/ui/EmptyState"; import { Panel } from "~/ui/Panel"; import { Button, buttonClass } from "~/ui/Button"; import { TextAreaField, TextField } from "~/ui/Field"; import { ErrorBanner } from "~/ui/Callout"; import { cn } from "~/utils/cn"; export const Route = createFileRoute("/$owner/$repo/milestones/")({ validateSearch: z.object({ state: z.enum(["open", "closed"]).optional() }), loaderDeps: ({ search }) => search, loader: ({ params, deps }) => listMilestones({ data: { owner: params.owner, repo: params.repo, state: deps.state ?? "open" } }), component: MilestonesPage, }); function dueDate(value: FormDataEntryValue | null): number | null { const text = String(value ?? ""); return text ? new Date(`${text}T00:00:00Z`).getTime() : null; } function NewMilestone({ owner, repo, onClose }: { owner: string; repo: string; onClose: () => void }) { const save = useAction(saveMilestone); return (
{ event.preventDefault(); const form = new FormData(event.currentTarget); const ok = await save.run({ data: { owner, repo, id: null, title: String(form.get("title") ?? ""), description: String(form.get("description") ?? ""), dueOn: dueDate(form.get("due")), state: "open", }, }); if (ok) onClose(); }} >
); } function MilestonesPage() { const { milestones, canMaintain } = Route.useLoaderData(); const params = Route.useParams(); const search = Route.useSearch(); const state = search.state ?? "open"; const save = useAction(saveMilestone); const remove = useAction(deleteMilestone); const [creating, setCreating] = useState(false); return (
{(["open", "closed"] as const).map((item) => ( {item} ))} {canMaintain && !creating && (
)}
{canMaintain && creating && ( setCreating(false)} /> )} {milestones.length === 0 ? ( Milestones group issues and pull requests toward a goal. ) : (
    {milestones.map((milestone) => (
  • {milestone.title}

    {milestone.dueOn ? `Due ${new Date(milestone.dueOn).toLocaleDateString()}` : "No due date"}

    {milestone.description && (

    {milestone.description}

    )}
    {canMaintain && (
    )}
  • ))}
)}
); }