Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ type BuildSettingsFieldsProps = {
* the pin status is unknown — distinct from "not set". */
currentTriggerVersionFetchFailed?: boolean;
/** Hide the section-level master toggles for "Pull env vars" and "Discover new env vars". */
hideSectionToggles?: boolean;
showAtomicDeployments?: boolean;
layout?: "settings" | "card";
};
Expand All @@ -68,7 +67,6 @@ export function BuildSettingsFields({
onAutoPromoteChange,
currentTriggerVersion,
currentTriggerVersionFetchFailed,
hideSectionToggles,
showAtomicDeployments = true,
layout = "card",
}: BuildSettingsFieldsProps) {
Expand Down Expand Up @@ -222,7 +220,7 @@ export function BuildSettingsFields({
<div className="mb-2">
<div className="flex items-center justify-between">
<Label>Pull env vars before build</Label>
{!hideSectionToggles && availableEnvSlugs.length > 1 && (
{availableEnvSlugs.length > 1 && (
<Switch
variant="small"
checked={
Expand Down Expand Up @@ -292,7 +290,7 @@ export function BuildSettingsFields({
<div className="mb-2">
<div className="flex items-center justify-between">
<Label>Discover new env vars</Label>
{!hideSectionToggles && availableEnvSlugs.length > 1 && (
{availableEnvSlugs.length > 1 && (
<Switch
variant="small"
checked={
Expand Down
70 changes: 29 additions & 41 deletions apps/webapp/app/components/integrations/VercelOnboardingModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
ChevronDownIcon,
ChevronUpIcon,
} from "@heroicons/react/20/solid";
import { useFetcher, useNavigation, useSearchParams } from "@remix-run/react";
import { useFetcher, useSearchParams } from "@remix-run/react";
import { useTypedFetcher } from "remix-typedjson";
import { Dialog, DialogContent, DialogHeader } from "~/components/primitives/Dialog";
import { Button, LinkButton } from "~/components/primitives/Buttons";
Expand Down Expand Up @@ -34,13 +34,11 @@ import {
type EnvSlug,
ALL_ENV_SLUGS,
shouldSyncEnvVarForAnyEnvironment,
getAvailableEnvSlugs,
getAvailableEnvSlugsForBuildSettings,
} from "~/v3/vercel/vercelProjectIntegrationSchema";
import { type VercelCustomEnvironment } from "~/models/vercelIntegration.server";
import { type VercelOnboardingData } from "~/presenters/v3/VercelSettingsPresenter.server";
import {
vercelAppInstallPath,
v3ProjectSettingsIntegrationsPath,
githubAppInstallPath,
vercelResourcePath,
Expand Down Expand Up @@ -78,7 +76,6 @@ function formatVercelTargets(targets: string[]): string {

type OnboardingState =
| "idle"
| "installing"
| "loading-projects"
| "project-selection"
| "loading-env-mapping"
Expand All @@ -99,6 +96,7 @@ export function VercelOnboardingModal({
hasStagingEnvironment,
hasPreviewEnvironment,
hasOrgIntegration,
onboardingDataUnavailable = false,
nextUrl,
onDataReload,
vercelManageAccessUrl,
Expand All @@ -112,16 +110,16 @@ export function VercelOnboardingModal({
hasStagingEnvironment: boolean;
hasPreviewEnvironment: boolean;
hasOrgIntegration: boolean;
/** The onboarding fetch settled without returning data - show an error instead of spinning. */
onboardingDataUnavailable?: boolean;
nextUrl?: string;
onDataReload?: (vercelStagingEnvironment?: string) => void;
vercelManageAccessUrl?: string;
}) {
const { capture, startSessionRecording } = usePostHogTracking();
const navigation = useNavigation();
const fetcher = useTypedFetcher<typeof loader>();
const envMappingFetcher = useFetcher();
const completeOnboardingFetcher = useFetcher();
const { Form: _CompleteOnboardingForm } = completeOnboardingFetcher;
const [searchParams] = useSearchParams();
const origin = searchParams.get("origin");
const fromMarketplaceContext = origin === "marketplace";
Expand All @@ -130,7 +128,6 @@ export function VercelOnboardingModal({
() => onboardingData?.availableProjects ?? [],
[onboardingData?.availableProjects]
);
const _hasProjectSelected = onboardingData?.hasProjectSelected ?? false;
const customEnvironments = useMemo(
() => onboardingData?.customEnvironments ?? [],
[onboardingData?.customEnvironments]
Expand Down Expand Up @@ -224,10 +221,6 @@ export function VercelOnboardingModal({
environmentId: string;
displayName: string;
} | null>(null);
const _availableEnvSlugsForOnboarding = getAvailableEnvSlugs(
hasStagingEnvironment,
hasPreviewEnvironment
);
const availableEnvSlugsForOnboardingBuildSettings = getAvailableEnvSlugsForBuildSettings(
hasStagingEnvironment,
hasPreviewEnvironment
Expand Down Expand Up @@ -375,7 +368,6 @@ export function VercelOnboardingModal({
}
break;

case "installing":
case "project-selection":
case "env-mapping":
case "env-var-sync":
Expand Down Expand Up @@ -459,8 +451,6 @@ export function VercelOnboardingModal({

const overlappingEnvVarsCount = enabledEnvVars.filter((v) => existingVars[v.key]).length;

const _isSubmitting = navigation.state === "submitting" || navigation.state === "loading";

const actionUrl = vercelResourcePath(organizationSlug, projectSlug, environmentSlug);

const handleToggleEnvVar = useCallback((key: string, enabled: boolean) => {
Expand Down Expand Up @@ -634,19 +624,6 @@ export function VercelOnboardingModal({
gitHubAppInstallations.length,
]);

const _handleFinishOnboarding = useCallback(
(e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const form = e.currentTarget;
const formData = new FormData(form);
completeOnboardingFetcher.submit(formData, {
method: "post",
action: actionUrl,
});
},
[completeOnboardingFetcher, actionUrl]
);

useEffect(() => {
if (
completeOnboardingFetcher.data &&
Expand Down Expand Up @@ -698,13 +675,6 @@ export function VercelOnboardingModal({
}
}, [state, onClose, trackOnboarding, isGitHubConnectedForOnboarding]);

useEffect(() => {
if (state === "installing") {
const installUrl = vercelAppInstallPath(organizationSlug, projectSlug);
window.location.href = installUrl;
}
}, [state, organizationSlug, projectSlug]);

useEffect(() => {
if (
envMappingFetcher.data &&
Expand Down Expand Up @@ -749,7 +719,6 @@ export function VercelOnboardingModal({
state === "loading-projects" ||
state === "loading-env-mapping" ||
state === "loading-env-vars" ||
state === "installing" ||
(state === "idle" && !onboardingData);

if (isLoadingState) {
Expand All @@ -758,9 +727,7 @@ export function VercelOnboardingModal({
open={isOpen}
onOpenChange={(open) => {
if (!open && !fromMarketplaceContext) {
if ((state as string) !== "completed") {
trackOnboarding("vercel onboarding abandoned");
}
trackOnboarding("vercel onboarding abandoned");
onClose();
}
}}
Expand All @@ -772,9 +739,30 @@ export function VercelOnboardingModal({
<span>Set up Vercel Integration</span>
</div>
</DialogHeader>
<div className="flex items-center justify-center py-8">
<Spinner color="blue" className="size-6" />
</div>
{onboardingDataUnavailable ? (
<div className="flex flex-col items-start gap-3 py-4">
<Paragraph variant="small">
We couldn't load your Vercel projects. The integration may have been removed or lost
access to this organization on Vercel.
</Paragraph>
<div className="flex items-center gap-2">
{onDataReload && (
<Button variant="secondary/small" onClick={() => onDataReload()}>
Try again
</Button>
)}
{vercelManageAccessUrl && (
<LinkButton to={vercelManageAccessUrl} target="_blank" variant="tertiary/small">
Manage access on Vercel
</LinkButton>
)}
</div>
</div>
) : (
<div className="flex items-center justify-center py-8">
<Spinner color="blue" className="size-6" />
</div>
)}
</DialogContent>
</Dialog>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ export class VercelSettingsPresenter extends BasePresenter {
})
).map((repo) => repo !== null);

// Match on slug, not type: preview branches are PREVIEW rows too, and only the
// branchable parent carries the "preview" slug. Keeps this in step with
// GitHubSettingsPresenter and ProjectSettingsService, which ask the same question.
const checkStagingEnvironment = () =>
fromPromise(
(this._replica as PrismaClient).runtimeEnvironment.findFirst({
Expand All @@ -181,7 +184,7 @@ export class VercelSettingsPresenter extends BasePresenter {
},
where: {
projectId,
type: "STAGING",
slug: "stg",
},
}),
(error) => ({
Expand All @@ -198,7 +201,7 @@ export class VercelSettingsPresenter extends BasePresenter {
},
where: {
projectId,
type: "PREVIEW",
slug: "preview",
},
}),
(error) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,10 @@ export default function IntegrationsSettingsPage() {
const loadVercelOnboarding = vercelFetcher.load;
const onboardingData = vercelFetcher.data?.onboardingData ?? null;
const hasVercelFetcherData = vercelFetcher.data !== undefined;
// The fetcher always requests `?vercelOnboarding=true`, so a settled load with no
// onboardingData means the presenter returned null - not that we simply didn't ask.
const onboardingDataUnavailable =
hasVercelFetcherData && vercelFetcher.state === "idle" && onboardingData === null;
const vercelOnboardingPath = `${vercelResourcePath(
organization.slug,
project.slug,
Expand Down Expand Up @@ -375,24 +379,26 @@ export default function IntegrationsSettingsPage() {
/>
</SettingsSection>
)}

<SettingsSection>
<SettingsHeader
title="Build settings"
description={
<>
Applies to deployments triggered from GitHub, and CLI deployments run with the{" "}
<InlineCode variant="extra-small" className="whitespace-nowrap">
--native-build-server
</InlineCode>{" "}
flag.
</>
}
/>
<BuildSettingsForm buildSettings={buildSettings ?? {}} />
</SettingsSection>
</>
)}

{/* Build settings also drive CLI deploys, so they stay available when the
GitHub app is disabled and the Git/Vercel sections above are hidden. */}
<SettingsSection>
<SettingsHeader
title="Build settings"
description={
<>
Applies to deployments triggered from GitHub, and CLI deployments run with the{" "}
<InlineCode variant="extra-small" className="whitespace-nowrap">
--native-build-server
</InlineCode>{" "}
flag.
</>
}
/>
<BuildSettingsForm buildSettings={buildSettings ?? {}} />
</SettingsSection>
</SettingsContainer>

{/* Vercel Onboarding Modal */}
Expand All @@ -407,6 +413,7 @@ export default function IntegrationsSettingsPage() {
hasStagingEnvironment={vercelFetcher.data?.hasStagingEnvironment ?? false}
hasPreviewEnvironment={vercelFetcher.data?.hasPreviewEnvironment ?? false}
hasOrgIntegration={vercelFetcher.data?.hasOrgIntegration ?? false}
onboardingDataUnavailable={onboardingDataUnavailable}
nextUrl={nextUrl ?? undefined}
vercelManageAccessUrl={vercelFetcher.data?.vercelManageAccessUrl}
onDataReload={(vercelEnvironmentId) => {
Expand Down
Loading