import { getFormProps, getInputProps, useForm } from "@conform-to/react"; import { parseWithZod } from "@conform-to/zod"; import { DialogClose } from "@radix-ui/react-dialog"; import { useFetcher, useLocation, useSearchParams } from "@remix-run/react"; import { type ActionFunctionArgs, json } from "@remix-run/server-runtime"; import { useEffect, useState } from "react"; import { InlineCode } from "~/components/code/InlineCode"; import { Button } from "~/components/primitives/Buttons"; import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "~/components/primitives/Dialog"; import { Fieldset } from "~/components/primitives/Fieldset"; import { FormButtons } from "~/components/primitives/FormButtons"; import { FormError } from "~/components/primitives/FormError"; import { Hint } from "~/components/primitives/Hint"; import { Input } from "~/components/primitives/Input"; import { InputGroup } from "~/components/primitives/InputGroup"; import { Label } from "~/components/primitives/Label"; import { useProject } from "~/hooks/useProject"; import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server"; import { requireUserId } from "~/services/session.server"; import { UpsertBranchService } from "~/services/upsertBranch.server"; import { type BranchableEnvironmentToken } from "~/utils/branchableEnvironment"; import { CreateBranchFormSchema } from "~/utils/branches"; import { branchesDevPath, branchesPath } from "~/utils/pathBuilder"; export async function action({ request }: ActionFunctionArgs) { const userId = await requireUserId(request); const formData = await request.formData(); const submission = parseWithZod(formData, { schema: CreateBranchFormSchema }); if (submission.status !== "success") { return redirectWithErrorMessage("/", request, "Invalid form data"); } const upsertBranchService = new UpsertBranchService(); const result = await upsertBranchService.call( { type: "userMembership", userId }, submission.value ); if (result.success) { if (result.alreadyExisted) { return json( submission.reply({ fieldErrors: { branchName: [ `Branch "${result.branch.branchName}" already exists. You can archive it and create a new one with the same name.`, ], }, }) ); } // Branches of both types are created through here; route the success // redirect to the matching list page based on the created branch's type. const path = result.branch.type === "DEVELOPMENT" ? branchesDevPath(result.organization, result.project, result.branch) : branchesPath(result.organization, result.project, result.branch); return redirectWithSuccessMessage( `${path}?dialogClosed=true`, request, `Branch "${result.branch.branchName}" created` ); } return json(submission.reply({ fieldErrors: { branchName: [result.error] } })); } export function NewBranchPanel({ button, env, }: { button: React.ReactNode; env: BranchableEnvironmentToken; }) { const project = useProject(); // Posts to this resource route (not the host page), so read the submission // result off the fetcher rather than the page's `useActionData`. const fetcher = useFetcher(); const lastSubmission = fetcher.data; const location = useLocation(); const [searchParams, setSearchParams] = useSearchParams(); const [isOpen, setIsOpen] = useState(false); const [form, { projectId, env: envField, branchName, failurePath }] = useForm({ id: "create-branch", lastResult: lastSubmission as any, onValidate({ formData }) { return parseWithZod(formData, { schema: CreateBranchFormSchema }); }, shouldRevalidate: "onInput", }); useEffect(() => { if (searchParams.has("dialogClosed")) { setSearchParams((s) => { s.delete("dialogClosed"); return s; }); setIsOpen(false); } }, [searchParams, setSearchParams]); return ( {button} New branch
Must not contain: spaces ~{" "} ^{" "} :{" "} ?{" "} *{" "} {"["}{" "} \{" "} //{" "} ..{" "} {"@{"}{" "} .lock {branchName.errors} {form.errors} Create branch } cancelButton={ } />
); }