Skip to content

Commit dbe29cc

Browse files
d-csTrigger.dev RepoOps
authored andcommitted
feat(webapp): guide first GitHub deployments in the dashboard
Mono-RevId: c77f5700f7d4a98e7e2a86685f4a7285960e82cd
1 parent 3fab9d4 commit dbe29cc

47 files changed

Lines changed: 4989 additions & 136 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/webapp/app/components/BlankStatePanels.tsx

Lines changed: 344 additions & 97 deletions
Large diffs are not rendered by default.

apps/webapp/app/components/dashboard-agent/AskAgentButton.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Button } from "~/components/primitives/Buttons";
1+
import { Button, type ButtonVariant } from "~/components/primitives/Buttons";
22
import { SimpleTooltip } from "~/components/primitives/Tooltip";
33
import { AgentIcon, ASK_AGENT_LABEL } from "./agent-identity";
44
import { requestDashboardAgent, useDashboardAgentAvailable } from "./dashboardAgentOpenRequest";
@@ -11,22 +11,24 @@ export function AskAgentButton({
1111
iconOnly = false,
1212
className,
1313
fallback = null,
14+
variant = "small-menu-item",
1415
}: {
1516
prompt?: string;
1617
label?: string;
1718
iconOnly?: boolean;
1819
className?: string;
1920
fallback?: React.ReactNode;
21+
variant?: ButtonVariant;
2022
}) {
2123
const available = useDashboardAgentAvailable();
2224
if (!available) return fallback;
2325

2426
const button = (
2527
<Button
2628
type="button"
27-
variant="small-menu-item"
29+
variant={variant}
2830
data-action="ask-agent"
29-
LeadingIcon={AgentIcon}
31+
LeadingIcon={variant.startsWith("ask-trigger/") ? undefined : AgentIcon}
3032
className={className}
3133
aria-label={iconOnly ? label : undefined}
3234
onClick={() => requestDashboardAgent(prompt)}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { cn } from "~/utils/cn";
2+
import type { ReactNode } from "react";
3+
import { EnvironmentIcon } from "~/components/environments/EnvironmentLabel";
4+
import { Header1 } from "~/components/primitives/Headers";
5+
import { ClientTabs, ClientTabsList, ClientTabsTrigger } from "~/components/primitives/ClientTabs";
6+
7+
export function DeploymentOnboardingFrame({
8+
title,
9+
heading,
10+
environment,
11+
help,
12+
children,
13+
enhanced = true,
14+
tabbed = true,
15+
}: {
16+
title: string;
17+
heading?: ReactNode;
18+
environment: React.ComponentProps<typeof EnvironmentIcon>["environment"];
19+
help?: ReactNode;
20+
children: ReactNode;
21+
enhanced?: boolean;
22+
tabbed?: boolean;
23+
}) {
24+
return (
25+
<>
26+
<div
27+
className={cn(
28+
"mb-2 flex items-center justify-between border-b",
29+
enhanced && "flex-wrap gap-x-8 gap-y-3 pb-2"
30+
)}
31+
>
32+
<div
33+
className={cn("flex min-w-0 items-center gap-2", enhanced ? "flex-1 basis-64" : "mb-2")}
34+
>
35+
<EnvironmentIcon environment={environment} className="-ml-1 size-8 shrink-0" />
36+
<Header1 className={enhanced ? "text-balance" : "truncate"}>
37+
{heading ?? `Deploy your tasks to ${title}`}
38+
</Header1>
39+
</div>
40+
{help && (
41+
<div className={enhanced ? "flex shrink-0 items-center gap-1" : "flex items-center"}>
42+
{help}
43+
</div>
44+
)}
45+
</div>
46+
{tabbed ? (
47+
<ClientTabs defaultValue="github">
48+
<ClientTabsList variant="segmented" className="mb-6">
49+
<ClientTabsTrigger value="github" variant="segmented" layoutId="deploy-tabs">
50+
GitHub
51+
</ClientTabsTrigger>
52+
<ClientTabsTrigger value="cli" variant="segmented" layoutId="deploy-tabs">
53+
Manual
54+
</ClientTabsTrigger>
55+
<ClientTabsTrigger value="github-actions" variant="segmented" layoutId="deploy-tabs">
56+
GitHub Actions
57+
</ClientTabsTrigger>
58+
</ClientTabsList>
59+
{children}
60+
</ClientTabs>
61+
) : (
62+
<div className="mt-6">{children}</div>
63+
)}
64+
</>
65+
);
66+
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import { SimpleTooltip } from "~/components/primitives/Tooltip";
2+
import { ArrowUpCircleIcon } from "@heroicons/react/20/solid";
3+
import type { ReactNode } from "react";
4+
import {
5+
EnvironmentIcon,
6+
environmentFullTitle,
7+
environmentTextClassName,
8+
} from "~/components/environments/EnvironmentLabel";
9+
import { LinkButton } from "~/components/primitives/Buttons";
10+
import { Hint } from "~/components/primitives/Hint";
11+
import {
12+
SettingsActions,
13+
SettingsBlock,
14+
SettingsRow,
15+
SettingsRowDescription,
16+
} from "~/components/primitives/SettingsLayout";
17+
import { cn } from "~/utils/cn";
18+
19+
/** The same environment availability gates serve onboarding, settings and the state gallery. */
20+
export function GitHubBranchTracking({
21+
productionInput,
22+
stagingInput,
23+
previewInput,
24+
stagingEnvironmentEnabled,
25+
previewEnvironmentEnabled,
26+
previewDeploymentsEnabled,
27+
billingPath,
28+
errors,
29+
saveAction,
30+
canManageGithub = true,
31+
}: {
32+
productionInput: ReactNode;
33+
stagingInput: ReactNode;
34+
previewInput: ReactNode;
35+
stagingEnvironmentEnabled?: boolean;
36+
previewEnvironmentEnabled?: boolean;
37+
previewDeploymentsEnabled: boolean;
38+
billingPath: string;
39+
errors?: ReactNode;
40+
saveAction?: ReactNode;
41+
canManageGithub?: boolean;
42+
}) {
43+
const upgrade = (
44+
<LinkButton
45+
to={billingPath}
46+
variant="secondary/small"
47+
LeadingIcon={ArrowUpCircleIcon}
48+
leadingIconClassName="text-indigo-500"
49+
>
50+
Upgrade
51+
</LinkButton>
52+
);
53+
const rowClass = "flex-wrap gap-3 [&>div:last-child]:max-w-full";
54+
return (
55+
<div data-testid="github-branch-tracking">
56+
<SettingsBlock size="sm">
57+
<Hint className="text-balance">
58+
Every push to the selected tracking branch creates a deployment in the corresponding
59+
environment.
60+
</Hint>
61+
</SettingsBlock>
62+
<SettingsRow
63+
className={rowClass}
64+
action={
65+
<GitHubPermissionTooltip denied={!canManageGithub}>
66+
{productionInput}
67+
</GitHubPermissionTooltip>
68+
}
69+
>
70+
<EnvironmentRowLabel type="PRODUCTION" />
71+
</SettingsRow>
72+
<SettingsRow
73+
className={rowClass}
74+
action={
75+
stagingEnvironmentEnabled ? (
76+
<GitHubPermissionTooltip denied={!canManageGithub}>
77+
{stagingInput}
78+
</GitHubPermissionTooltip>
79+
) : (
80+
upgrade
81+
)
82+
}
83+
>
84+
<EnvironmentRowLabel
85+
type="STAGING"
86+
description={
87+
stagingEnvironmentEnabled
88+
? undefined
89+
: "Upgrade your plan to enable a Staging environment"
90+
}
91+
/>
92+
</SettingsRow>
93+
<SettingsRow
94+
className={rowClass}
95+
action={
96+
previewEnvironmentEnabled ? (
97+
<GitHubPermissionTooltip denied={!canManageGithub}>
98+
{previewInput}
99+
</GitHubPermissionTooltip>
100+
) : (
101+
<>
102+
{previewDeploymentsEnabled && (
103+
<input type="hidden" name="previewDeploymentsEnabled" value="on" />
104+
)}
105+
{upgrade}
106+
</>
107+
)
108+
}
109+
>
110+
<EnvironmentRowLabel
111+
type="PREVIEW"
112+
description={
113+
previewEnvironmentEnabled ? undefined : "Upgrade your plan to enable preview branches"
114+
}
115+
/>
116+
</SettingsRow>
117+
{errors}
118+
{saveAction && <SettingsActions>{saveAction}</SettingsActions>}
119+
</div>
120+
);
121+
}
122+
123+
function EnvironmentRowLabel({
124+
type,
125+
description,
126+
}: {
127+
type: "PRODUCTION" | "STAGING" | "PREVIEW";
128+
description?: ReactNode;
129+
}) {
130+
return (
131+
<div className="min-w-0 flex-1 space-y-1">
132+
<div className="flex items-center gap-1.5">
133+
<EnvironmentIcon environment={{ type }} className="size-4" />
134+
<span className={cn("text-sm", environmentTextClassName({ type }))}>
135+
{environmentFullTitle({ type })}
136+
</span>
137+
</div>
138+
{description && <SettingsRowDescription>{description}</SettingsRowDescription>}
139+
</div>
140+
);
141+
}
142+
143+
/** Disabled native controls don't receive pointer events; the wrapper owns the tooltip. */
144+
export function GitHubPermissionTooltip({
145+
denied,
146+
children,
147+
}: {
148+
denied: boolean;
149+
children: ReactNode;
150+
}) {
151+
if (!denied) return children;
152+
return (
153+
<SimpleTooltip
154+
asChild
155+
disableHoverableContent
156+
content="You don't have permission to manage GitHub settings."
157+
button={
158+
<span className="inline-flex max-w-full cursor-not-allowed [&>*]:pointer-events-none">
159+
{children}
160+
</span>
161+
}
162+
/>
163+
);
164+
}

0 commit comments

Comments
 (0)