Skip to content

Commit 8b4f652

Browse files
authored
Merge branch 'main' into feat/external-contributor-protection
2 parents 8ca4d20 + dca6362 commit 8b4f652

File tree

15 files changed

+463
-253
lines changed

15 files changed

+463
-253
lines changed

web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/[deploymentId]/(deployment-progress)/deployment-step.tsx

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { cn } from "@/lib/utils";
44
import { formatCompoundDuration } from "@/lib/utils/metric-formatters";
55
import { Check, CircleHalfDottedClock, TriangleWarning2 } from "@unkey/icons";
66
import { Badge, Loading, SettingCard } from "@unkey/ui";
7+
import { GlowIcon } from "../../../../components/glow-icon";
78

89
type DeploymentStepProps = {
910
icon: React.ReactNode;
@@ -32,28 +33,13 @@ export function DeploymentStep({
3233
<SettingCard
3334
truncateDescription
3435
icon={
35-
<div className="relative w-full h-full">
36-
<div
37-
className={cn(
38-
"absolute inset-[-4px] rounded-[10px] blur-[14px]",
39-
isError
40-
? "bg-linear-to-l from-error-7 to-error-8"
41-
: "bg-linear-to-l from-feature-8 to-info-9",
42-
showGlow ? "animate-pulse opacity-20" : "opacity-0 transition-opacity duration-300",
43-
)}
44-
/>
45-
<div
46-
className={cn(
47-
"w-full h-full rounded-[10px] flex items-center justify-center shrink-0",
48-
isError
49-
? "relative bg-errorA-3 dark:text-error-11 text-error-11"
50-
: showGlow &&
51-
"relative dark:bg-white dark:text-black bg-black text-white shadow-md shadow-black/40",
52-
)}
53-
>
54-
{icon}
55-
</div>
56-
</div>
36+
<GlowIcon
37+
icon={icon}
38+
variant={isError ? "error" : "feature"}
39+
glow={showGlow}
40+
transition
41+
className="w-full h-full"
42+
/>
5743
}
5844
title={
5945
<div className="flex items-center gap-2">

web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/components/table/components/actions/redeploy-dialog.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export const RedeployDialog = ({ isOpen, onClose, selectedDeployment }: Redeploy
2323
const redeploy = trpc.deploy.deployment.redeploy.useMutation({
2424
onSuccess: async (data) => {
2525
await queryClient.invalidateQueries({ queryKey: ["deployments", projectId] });
26+
onClose();
2627
router.push(
2728
`/${workspace.slug}/projects/${selectedDeployment.projectId}/deployments/${data.deploymentId}`,
2829
);

web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/environment-provider.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useProjectData } from "../data-provider";
1010
type EnvironmentContextType = {
1111
settings: EnvironmentSettings;
1212
variant: "settings" | "onboarding";
13+
isSaving: boolean;
1314
};
1415

1516
export const EnvironmentContext = createContext<EnvironmentContextType | null>(null);
@@ -44,7 +45,7 @@ export const EnvironmentSettingsProvider = ({ children }: PropsWithChildren) =>
4445
}
4546

4647
return (
47-
<EnvironmentContext.Provider value={{ settings, variant: "settings" }}>
48+
<EnvironmentContext.Provider value={{ settings, variant: "settings", isSaving: false }}>
4849
{children}
4950
</EnvironmentContext.Provider>
5051
);

web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { DeploymentSettings } from "./deployment-settings";
44
import { EnvironmentSettingsProvider } from "./environment-provider";
5+
import { PendingRedeployBanner } from "./pending-redeploy-banner";
56

67
export default function SettingsPage() {
78
return (
@@ -15,6 +16,7 @@ export default function SettingsPage() {
1516
</div>
1617
<DeploymentSettings />
1718
</div>
19+
<PendingRedeployBanner />
1820
</EnvironmentSettingsProvider>
1921
);
2022
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"use client";
2+
3+
import { useWorkspaceNavigation } from "@/hooks/use-workspace-navigation";
4+
import { queryClient } from "@/lib/collections/client";
5+
import { useSettingsHasSaved } from "@/lib/collections/deploy/environment-settings";
6+
import { trpc } from "@/lib/trpc/client";
7+
import { Hammer2, XMark } from "@unkey/icons";
8+
import { Button, toast } from "@unkey/ui";
9+
import { useRouter } from "next/navigation";
10+
import { useState } from "react";
11+
import { GlowIcon } from "../../components/glow-icon";
12+
import { useProjectData } from "../data-provider";
13+
14+
export function PendingRedeployBanner() {
15+
const [dismissed, setDismissed] = useState(false);
16+
const { project, deployments, projectId } = useProjectData();
17+
const router = useRouter();
18+
const workspace = useWorkspaceNavigation();
19+
const hasSaved = useSettingsHasSaved();
20+
const visible = hasSaved && !dismissed;
21+
22+
const currentDeployment = project?.currentDeploymentId
23+
? deployments.find((d) => d.id === project.currentDeploymentId)
24+
: undefined;
25+
26+
const redeploy = trpc.deploy.deployment.redeploy.useMutation({
27+
onSuccess: async (data) => {
28+
if (!currentDeployment) {
29+
return;
30+
}
31+
await queryClient.invalidateQueries({ queryKey: ["deployments", projectId] });
32+
router.push(
33+
`/${workspace.slug}/projects/${currentDeployment.projectId}/deployments/${data.deploymentId}`,
34+
);
35+
},
36+
onError: (error) => {
37+
toast.error("Redeploy failed", { description: error.message });
38+
},
39+
});
40+
41+
if (!visible || !currentDeployment) {
42+
return null;
43+
}
44+
45+
return (
46+
<div className="fixed top-6 right-6 z-50 animate-fade-slide-in">
47+
<div className="relative flex items-start gap-4 rounded-xl border border-gray-4 bg-gray-1 p-4 shadow-lg w-100">
48+
<button
49+
type="button"
50+
onClick={() => setDismissed(true)}
51+
className="absolute top-3 right-3 text-gray-9 hover:text-gray-11 transition-colors cursor-pointer"
52+
aria-label="Dismiss"
53+
>
54+
<XMark className="size-4" />
55+
</button>
56+
57+
<GlowIcon
58+
icon={<Hammer2 iconSize="sm-medium" className="size-[18px]" />}
59+
className="w-9 h-9 shrink-0"
60+
/>
61+
62+
<div className="flex flex-col gap-3 flex-1 min-w-0">
63+
<div className="flex flex-col gap-1 pr-5">
64+
<span className="text-sm font-semibold text-gray-12 leading-5">Settings changed</span>
65+
<span className="text-xs text-gray-11 leading-4">
66+
Redeploy to apply your latest settings to production.
67+
</span>
68+
</div>
69+
<Button
70+
variant="primary"
71+
size="md"
72+
className="w-full"
73+
disabled={redeploy.isLoading}
74+
loading={redeploy.isLoading}
75+
onClick={() => {
76+
redeploy.mutate({ deploymentId: currentDeployment.id });
77+
}}
78+
>
79+
Redeploy
80+
</Button>
81+
</div>
82+
</div>
83+
</div>
84+
);
85+
}

web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/components/deployment-domains-card.tsx

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"use client";
22

33
import type { Domain } from "@/lib/collections";
4-
import { cn } from "@/lib/utils";
54
import { ChevronDown, Cube, Earth, Link4 } from "@unkey/icons";
65
import {
76
Button,
@@ -17,6 +16,7 @@ import { useProjectData } from "../(overview)/data-provider";
1716
import { useDeployment } from "../(overview)/deployments/[deploymentId]/layout-provider";
1817
import { SettingsGroup } from "../(overview)/settings/components/shared/settings-group";
1918
import { getDomainPriority } from "./domain-priority";
19+
import { GlowIcon } from "./glow-icon";
2020
import { TagBadge } from "./tag-badge";
2121

2222
export function DeploymentDomainsCard({
@@ -73,28 +73,11 @@ export function DeploymentDomainsCard({
7373
<SettingCard
7474
iconClassName={glow ? "bg-transparent shadow-none dark:ring-0" : undefined}
7575
icon={
76-
glow ? (
77-
<div className="relative w-full h-full">
78-
<div
79-
className={cn(
80-
"absolute inset-[-4px] rounded-[10px] blur-[14px]",
81-
"bg-linear-to-l from-feature-8 to-info-9",
82-
"animate-pulse opacity-20",
83-
)}
84-
/>
85-
<div
86-
className={cn(
87-
"w-full h-full rounded-[10px] flex items-center justify-center shrink-0 relative dark:bg-white dark:text-black bg-black text-white",
88-
)}
89-
>
90-
<Cube iconSize="md-medium" className="size-[18px]" />
91-
</div>
92-
</div>
93-
) : (
94-
<div className="w-full h-full rounded-[10px] flex items-center justify-center shrink-0">
95-
<Cube iconSize="md-medium" className="size-[18px]" />
96-
</div>
97-
)
76+
<GlowIcon
77+
icon={<Cube iconSize="md-medium" className="size-[18px]" />}
78+
glow={glow}
79+
className="w-full h-full"
80+
/>
9881
}
9982
title={project?.name}
10083
description={
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { cn } from "@/lib/utils";
2+
import type { ReactNode } from "react";
3+
4+
type GlowIconProps = {
5+
icon: ReactNode;
6+
variant?: "feature" | "error";
7+
glow?: boolean;
8+
transition?: boolean;
9+
className?: string;
10+
};
11+
12+
export function GlowIcon({
13+
icon,
14+
variant = "feature",
15+
glow = true,
16+
transition = false,
17+
className,
18+
}: GlowIconProps) {
19+
const glowColor =
20+
variant === "error"
21+
? "bg-linear-to-l from-error-7 to-error-8"
22+
: "bg-linear-to-l from-feature-8 to-info-9";
23+
24+
const glowVisible = transition
25+
? glow
26+
? "animate-pulse opacity-20"
27+
: "opacity-0 transition-opacity duration-300"
28+
: glow
29+
? "animate-pulse opacity-20"
30+
: "hidden";
31+
32+
const iconBg =
33+
variant === "error"
34+
? "bg-errorA-3 dark:text-error-11 text-error-11"
35+
: glow
36+
? "dark:bg-white dark:text-black bg-black text-white shadow-md shadow-black/40"
37+
: "";
38+
39+
return (
40+
<div className={cn("relative", className)}>
41+
<div
42+
className={cn("absolute inset-[-4px] rounded-[10px] blur-[14px]", glowColor, glowVisible)}
43+
/>
44+
<div
45+
className={cn(
46+
"relative w-full h-full rounded-[10px] flex items-center justify-center shrink-0",
47+
iconBg,
48+
)}
49+
>
50+
{icon}
51+
</div>
52+
</div>
53+
);
54+
}

web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/configure-deployment.tsx

Lines changed: 0 additions & 61 deletions
This file was deleted.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"use client";
2+
3+
import { queryClient } from "@/lib/collections/client";
4+
import { trpc } from "@/lib/trpc/client";
5+
import { Button, toast, useStepWizard } from "@unkey/ui";
6+
import { DeploymentSettings } from "../../../[projectId]/(overview)/settings/deployment-settings";
7+
import { useEnvironmentSettings } from "../../../[projectId]/(overview)/settings/environment-provider";
8+
9+
type ConfigureDeploymentContentProps = {
10+
projectId: string;
11+
onDeploymentCreated: (deploymentId: string) => void;
12+
};
13+
14+
export const ConfigureDeploymentContent = ({
15+
projectId,
16+
onDeploymentCreated,
17+
}: ConfigureDeploymentContentProps) => {
18+
const { next } = useStepWizard();
19+
const { isSaving } = useEnvironmentSettings();
20+
21+
const deploy = trpc.deploy.deployment.create.useMutation({
22+
onSuccess: async (data) => {
23+
await queryClient.invalidateQueries({ queryKey: ["deployments", projectId] });
24+
toast.success("Deployment triggered", {
25+
description: "Your project is being built and deployed",
26+
});
27+
onDeploymentCreated(data.deploymentId);
28+
next();
29+
},
30+
onError: (error) => {
31+
toast.error("Deployment failed", { description: error.message });
32+
},
33+
});
34+
35+
return (
36+
<div className="w-225">
37+
<DeploymentSettings githubReadOnly sections={{ build: true }} />
38+
<div className="flex justify-end mt-6 mb-10 flex-col gap-4">
39+
<Button
40+
type="button"
41+
variant="primary"
42+
size="xlg"
43+
className="rounded-lg"
44+
disabled={deploy.isLoading || isSaving}
45+
loading={deploy.isLoading}
46+
onClick={() => deploy.mutate({ projectId, environmentSlug: "production" })}
47+
>
48+
Deploy
49+
</Button>
50+
<span className="text-gray-10 text-[13px] text-center">
51+
We'll build your image, provision infrastructure, and more.
52+
<br />
53+
</span>
54+
</div>
55+
</div>
56+
);
57+
};

0 commit comments

Comments
 (0)