| 1 | // The forms of the workspace's milestones pages, under Work: create, edit, |
| 2 | // close or reopen, delete. Each needs the Write role in the workspace, |
| 3 | // which work checks again. |
| 4 | |
| 5 | import { needs } from "@g1t/contracts"; |
| 6 | |
| 7 | import type { MilestoneResult } from "../components/milestones"; |
| 8 | import { work } from "./services.server"; |
| 9 | import { assertSameOrigin, requireUser } from "./session.server"; |
| 10 | import { canItem } from "./work-access"; |
| 11 | |
| 12 | type Context = Parameters<typeof requireUser>[0]; |
| 13 | |
| 14 | export async function milestoneAction(request: Request, params: { owner?: string }, context: Context): Promise<MilestoneResult> { |
| 15 | assertSameOrigin(request); |
| 16 | const user = requireUser(context, request); |
| 17 | const form = await request.formData(); |
| 18 | const intent = String(form.get("intent") ?? ""); |
| 19 | const number = Number(form.get("number")) || undefined; |
| 20 | const workspace = (params.owner ?? "").toLowerCase(); |
| 21 | if (!canItem(user, workspace, null, "manage_labels")) return { intent, number, error: needs("manage_labels") }; |
| 22 | const text = (key: string) => (form.has(key) ? String(form.get(key) ?? "") : undefined); |
| 23 | if (intent === "delete") { |
| 24 | const deleted = await work.deleteMilestone(user, workspace, number ?? 0); |
| 25 | return deleted.ok ? { intent, number } : { intent, number, error: deleted.error.message }; |
| 26 | } |
| 27 | const state = form.get("state"); |
| 28 | const saved = await work.saveMilestone(user, workspace, { |
| 29 | number: intent === "create" ? undefined : number, |
| 30 | title: text("title"), |
| 31 | description: text("description"), |
| 32 | dueOn: text("dueOn"), |
| 33 | state: state === "open" || state === "closed" ? state : undefined, |
| 34 | }); |
| 35 | return saved.ok ? { intent, number: saved.value.number } : { intent, number, error: saved.error.message }; |
| 36 | } |