Skip to content

Commit b0eef85

Browse files
committed
Enable updating git settings
1 parent 7bf14ab commit b0eef85

File tree

1 file changed

+118
-29
lines changed
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings

1 file changed

+118
-29
lines changed

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx

Lines changed: 118 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { z } from "zod";
2323
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
2424
import { InlineCode } from "~/components/code/InlineCode";
2525
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "~/components/primitives/Dialog";
26-
import { DialogClose, DialogDescription } from "@radix-ui/react-dialog";
26+
import { DialogClose } from "@radix-ui/react-dialog";
2727
import { OctoKitty } from "~/components/GitHubLoginButton";
2828
import {
2929
MainHorizontallyCenteredContainer,
@@ -49,6 +49,7 @@ import { useOrganization } from "~/hooks/useOrganizations";
4949
import { useProject } from "~/hooks/useProject";
5050
import {
5151
redirectBackWithErrorMessage,
52+
redirectBackWithSuccessMessage,
5253
redirectWithErrorMessage,
5354
redirectWithSuccessMessage,
5455
} from "~/models/message.server";
@@ -62,9 +63,16 @@ import {
6263
githubAppInstallPath,
6364
EnvironmentParamSchema,
6465
} from "~/utils/pathBuilder";
65-
import { useEffect, useState } from "react";
66+
import React, { useEffect, useState } from "react";
6667
import { Select, SelectItem } from "~/components/primitives/Select";
68+
import { Switch } from "~/components/primitives/Switch";
6769
import { BranchTrackingConfigSchema, type BranchTrackingConfig } from "~/v3/github";
70+
import {
71+
EnvironmentIcon,
72+
environmentFullTitle,
73+
environmentTextClassName,
74+
} from "~/components/environments/EnvironmentLabel";
75+
import { GitBranchIcon } from "lucide-react";
6876

6977
export const meta: MetaFunction = () => {
7078
return [
@@ -138,9 +146,6 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
138146
htmlUrl: true,
139147
private: true,
140148
},
141-
where: {
142-
removedAt: null,
143-
},
144149
// Most installations will only have a couple of repos so loading them here should be fine.
145150
// However, there might be outlier organizations so it's best to expose the installation repos
146151
// via a resource endpoint and filter on user input.
@@ -156,6 +161,17 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
156161
return typedjson({ githubAppInstallations, connectedGithubRepository: undefined });
157162
};
158163

164+
const UpdateGitSettingsFormSchema = z.object({
165+
action: z.literal("update-git-settings"),
166+
projectId: z.string(),
167+
productionBranch: z.string().min(1, "Production branch is required"),
168+
stagingBranch: z.string().min(1, "Staging branch is required"),
169+
previewDeploymentsEnabled: z
170+
.string()
171+
.optional()
172+
.transform((val) => val === "on"),
173+
});
174+
159175
export function createSchema(
160176
constraints: {
161177
getSlugMatch?: (slug: string) => { isMatch: boolean; projectSlug: string };
@@ -193,6 +209,7 @@ export function createSchema(
193209
repositoryId: z.string().min(1, "Repository is required"),
194210
projectId: z.string().min(1, "Project ID is required"),
195211
}),
212+
UpdateGitSettingsFormSchema,
196213
]);
197214
}
198215

@@ -276,6 +293,35 @@ export const action: ActionFunction = async ({ request, params }) => {
276293
);
277294
}
278295
}
296+
case "update-git-settings": {
297+
const { projectId, productionBranch, stagingBranch, previewDeploymentsEnabled } =
298+
submission.value;
299+
300+
const existingConnection = await prisma.connectedGithubRepository.findFirst({
301+
where: {
302+
projectId: projectId,
303+
},
304+
});
305+
306+
if (!existingConnection) {
307+
return redirectBackWithErrorMessage(request, "No connected GitHub repository found");
308+
}
309+
310+
await prisma.connectedGithubRepository.update({
311+
where: {
312+
projectId: projectId,
313+
},
314+
data: {
315+
branchTracking: {
316+
production: { branch: productionBranch },
317+
staging: { branch: stagingBranch },
318+
} satisfies BranchTrackingConfig,
319+
previewDeploymentsEnabled: previewDeploymentsEnabled,
320+
},
321+
});
322+
323+
return redirectBackWithSuccessMessage(request, "Git settings updated successfully");
324+
}
279325
case "connect-repo": {
280326
const { repositoryId, projectId } = submission.value;
281327

@@ -343,7 +389,6 @@ export default function Page() {
343389
const organization = useOrganization();
344390
const lastSubmission = useActionData();
345391
const navigation = useNavigation();
346-
const location = useLocation();
347392

348393
const [renameForm, { projectName }] = useForm({
349394
id: "rename-project",
@@ -458,7 +503,6 @@ export default function Page() {
458503
{connectedGithubRepository ? (
459504
<ConnectedGitHubRepoForm
460505
connectedGitHubRepo={connectedGithubRepository}
461-
organizationSlug={organization.slug}
462506
projectId={project.id}
463507
/>
464508
) : (
@@ -752,34 +796,32 @@ type ConnectedGitHubRepo = {
752796

753797
function ConnectedGitHubRepoForm({
754798
connectedGitHubRepo,
755-
organizationSlug,
756799
projectId,
757800
}: {
758801
connectedGitHubRepo: ConnectedGitHubRepo;
759-
organizationSlug: string;
760802
projectId: string;
761803
}) {
762804
const lastSubmission = useActionData() as any;
763805
const navigation = useNavigation();
764806

765-
const [renameForm, { projectName }] = useForm({
766-
id: "rename-project",
807+
const [gitSettingsForm, fields] = useForm({
808+
id: "update-git-settings",
767809
lastSubmission: lastSubmission,
768810
shouldRevalidate: "onSubmit",
769811
onValidate({ formData }) {
770812
return parse(formData, {
771-
schema: createSchema(),
813+
schema: UpdateGitSettingsFormSchema,
772814
});
773815
},
774816
});
775817

776-
const isRenameLoading =
777-
navigation.formData?.get("action") === "rename" &&
818+
const isGitSettingsLoading =
819+
navigation.formData?.get("action") === "update-git-settings" &&
778820
(navigation.state === "submitting" || navigation.state === "loading");
779821

780822
return (
781823
<>
782-
<div className="flex items-center justify-between rounded-sm border bg-grid-dimmed p-2">
824+
<div className="mb-4 flex items-center justify-between rounded-sm border bg-grid-dimmed p-2">
783825
<div className="flex items-center gap-2">
784826
<OctoKitty className="size-4" />
785827
<a
@@ -789,31 +831,78 @@ function ConnectedGitHubRepoForm({
789831
>
790832
{connectedGitHubRepo.repository.fullName}
791833
</a>
834+
{connectedGitHubRepo.repository.private && (
835+
<LockClosedIcon className="size-3 text-text-dimmed" />
836+
)}
792837
</div>
793838
<Button variant="minimal/small">Disconnect</Button>
794839
</div>
795-
<Form method="post" {...renameForm.props} className="mt-4">
840+
841+
<Form method="post" {...gitSettingsForm.props}>
842+
<input type="hidden" name="projectId" value={projectId} />
796843
<Fieldset>
797844
<InputGroup fullWidth>
798-
<Label htmlFor={projectName.id}>Project name</Label>
799-
<Input
800-
{...conform.input(projectName, { type: "text" })}
801-
defaultValue={"asdf"}
802-
placeholder="Project name"
803-
icon={FolderIcon}
804-
autoFocus
805-
/>
806-
<FormError id={projectName.errorId}>{projectName.error}</FormError>
845+
<Hint>
846+
Every commit on the selected tracking branch creates a deployment in the corresponding
847+
environment.
848+
</Hint>
849+
<div className="grid grid-cols-[120px_1fr] gap-3">
850+
<div className="flex items-center gap-1.5">
851+
<EnvironmentIcon environment={{ type: "PRODUCTION" }} className="size-4" />
852+
<span className={`text-sm ${environmentTextClassName({ type: "PRODUCTION" })}`}>
853+
{environmentFullTitle({ type: "PRODUCTION" })}
854+
</span>
855+
</div>
856+
<Input
857+
{...conform.input(fields.productionBranch, { type: "text" })}
858+
defaultValue={connectedGitHubRepo.branchTracking?.production?.branch}
859+
placeholder="Branch name"
860+
variant="tertiary"
861+
icon={GitBranchIcon}
862+
/>
863+
<div className="flex items-center gap-1.5">
864+
<EnvironmentIcon environment={{ type: "STAGING" }} className="size-4" />
865+
<span className={`text-sm ${environmentTextClassName({ type: "STAGING" })}`}>
866+
{environmentFullTitle({ type: "STAGING" })}
867+
</span>
868+
</div>
869+
<Input
870+
{...conform.input(fields.stagingBranch, { type: "text" })}
871+
defaultValue={connectedGitHubRepo.branchTracking?.staging?.branch}
872+
placeholder="Branch name"
873+
variant="tertiary"
874+
icon={GitBranchIcon}
875+
/>
876+
877+
<div className="flex items-center gap-1.5">
878+
<EnvironmentIcon environment={{ type: "PREVIEW" }} className="size-4" />
879+
<span className={`text-sm ${environmentTextClassName({ type: "PREVIEW" })}`}>
880+
{environmentFullTitle({ type: "PREVIEW" })}
881+
</span>
882+
</div>
883+
<Switch
884+
name="previewDeploymentsEnabled"
885+
defaultChecked={connectedGitHubRepo.previewDeploymentsEnabled}
886+
variant="small"
887+
label="create preview deployments for pull requests"
888+
labelPosition="right"
889+
/>
890+
</div>
891+
<FormError>{fields.productionBranch?.error}</FormError>
892+
<FormError>{fields.stagingBranch?.error}</FormError>
893+
<FormError>{fields.previewDeploymentsEnabled?.error}</FormError>
894+
<FormError>{gitSettingsForm.error}</FormError>
807895
</InputGroup>
896+
808897
<FormButtons
809898
confirmButton={
810899
<Button
811900
type="submit"
812901
name="action"
813-
value="rename"
814-
variant={"secondary/small"}
815-
disabled={isRenameLoading}
816-
LeadingIcon={isRenameLoading ? SpinnerWhite : undefined}
902+
value="update-git-settings"
903+
variant="secondary/small"
904+
disabled={isGitSettingsLoading}
905+
LeadingIcon={isGitSettingsLoading ? SpinnerWhite : undefined}
817906
>
818907
Save
819908
</Button>

0 commit comments

Comments
 (0)