From e187c10844e7ff81bc698db8a05514b66258c24f Mon Sep 17 00:00:00 2001 From: ogzhanolguncu Date: Wed, 4 Mar 2026 13:46:37 +0300 Subject: [PATCH 01/12] refactor: dont show some settings when landed there initially --- .../components/shared/settings-group.tsx | 10 ++- .../settings/deployment-settings.tsx | 76 ++++++++++--------- .../new/steps/configure-deployment.tsx | 4 +- 3 files changed, 49 insertions(+), 41 deletions(-) diff --git a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/shared/settings-group.tsx b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/shared/settings-group.tsx index 6c95af15b81..95e6162747b 100644 --- a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/shared/settings-group.tsx +++ b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/shared/settings-group.tsx @@ -1,7 +1,7 @@ "use client"; import { ChevronRight } from "@unkey/icons"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; type SettingsGroupProps = { icon: React.ReactNode; @@ -20,6 +20,10 @@ export const SettingsGroup = ({ }: SettingsGroupProps) => { const [expanded, setExpanded] = useState(defaultExpanded); + useEffect(() => { + setExpanded(defaultExpanded); + }, [defaultExpanded]); + return (
@@ -30,13 +34,13 @@ export const SettingsGroup = ({ - -
+ + +
+ )} } > diff --git a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/deployment-settings.tsx b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/deployment-settings.tsx index 7bfb22778f0..f3097376c2e 100644 --- a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/deployment-settings.tsx +++ b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/deployment-settings.tsx @@ -29,8 +29,10 @@ type DeploymentSettingsProps = { sections?: Partial>; }; - -export const DeploymentSettings = ({ githubReadOnly = false, sections = { build: true, runtime: true, advanced: true, sentinel: true } }: DeploymentSettingsProps) => { +export const DeploymentSettings = ({ + githubReadOnly = false, + sections = { build: true, runtime: true, advanced: true, sentinel: true }, +}: DeploymentSettingsProps) => { return (
@@ -38,7 +40,11 @@ export const DeploymentSettings = ({ githubReadOnly = false, sections = { build: - } title="Runtime settings" defaultExpanded={Boolean(sections.runtime)}> + } + title="Runtime settings" + defaultExpanded={Boolean(sections.runtime)} + > @@ -53,17 +59,25 @@ export const DeploymentSettings = ({ githubReadOnly = false, sections = { build: {/* */} - } title="Advanced configurations" defaultExpanded={Boolean(sections.advanced)}> + } + title="Advanced configurations" + defaultExpanded={Boolean(sections.advanced)} + > - } title="Sentinel configurations" defaultExpanded={Boolean(sections.sentinel)}> + } + title="Sentinel configurations" + defaultExpanded={Boolean(sections.sentinel)} + >
- ) -} + ); +}; diff --git a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/environment-provider.tsx b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/environment-provider.tsx index 1669035daf7..4f41c62384a 100644 --- a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/environment-provider.tsx +++ b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/environment-provider.tsx @@ -9,6 +9,7 @@ import { useProjectData } from "../data-provider"; type EnvironmentContextType = { settings: EnvironmentSettings; + autoSave?: boolean; }; export const EnvironmentContext = createContext(null); diff --git a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/onboarding-environment-provider.tsx b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/onboarding-environment-provider.tsx index 33838606196..9c3c9a0c72c 100644 --- a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/onboarding-environment-provider.tsx +++ b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/onboarding-environment-provider.tsx @@ -4,6 +4,7 @@ import { type EnvironmentSettings, buildSettingsMutations, } from "@/lib/collections/deploy/environment-settings"; +import { trpc } from "@/lib/trpc/client"; import { eq, useLiveQuery } from "@tanstack/react-db"; import { type PropsWithChildren, useEffect, useMemo, useRef } from "react"; import { useProjectData } from "../../[projectId]/(overview)/data-provider"; @@ -39,13 +40,41 @@ export const OnboardingEnvironmentSettingsProvider = ({ children }: PropsWithChi const settings = data.at(0); + const { data: availableRegions } = trpc.deploy.environmentSettings.getAvailableRegions.useQuery( + undefined, + { enabled: Boolean(prodEnvId) }, + ); + + const hasInitializedRegionsRef = useRef(false); + + // Settings are empty initially so we set all of them by default for the user. + // Later he can change it in the settings + useEffect(() => { + if (!settings || !availableRegions || hasInitializedRegionsRef.current) { + return; + } + hasInitializedRegionsRef.current = true; + if (Object.keys(settings.regionConfig).length > 0) { + return; + } + collection.environmentSettings.update(settings.environmentId, (draft) => { + for (const region of availableRegions) { + draft.regionConfig[region] = 1; + } + }); + }, [settings, availableRegions]); + useSyncSettingsToOtherEnvironments(settings, otherEnvIds); if (!settings) { return null; } - return {children}; + return ( + + {children} + + ); }; function useSyncSettingsToOtherEnvironments( From 7a1b8fdeb2c912b3ea628d19771503b0bdfe3cf1 Mon Sep 17 00:00:00 2001 From: ogzhanolguncu Date: Wed, 4 Mar 2026 15:17:55 +0300 Subject: [PATCH 03/12] feat: add default settings when you land on configure --- .../new/steps/configure-deployment.tsx | 4 +- .../steps/onboarding-environment-provider.tsx | 75 ++++++++++++++----- .../integrations/github/callback/page.tsx | 1 - .../deploy/environment-settings.ts | 22 +++--- 4 files changed, 70 insertions(+), 32 deletions(-) diff --git a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/configure-deployment.tsx b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/configure-deployment.tsx index 504ccc2eb0c..5e2e5385fd2 100644 --- a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/configure-deployment.tsx +++ b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/configure-deployment.tsx @@ -16,7 +16,7 @@ export const ConfigureDeploymentStep = ({ projectId, onDeploymentCreated, }: ConfigureDeploymentStepProps) => { - const { next } = useStepWizard(); + const { next, activeStepId } = useStepWizard(); const deploy = trpc.deploy.deployment.create.useMutation({ onSuccess: async (data) => { @@ -34,7 +34,7 @@ export const ConfigureDeploymentStep = ({ return ( - +
diff --git a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/onboarding-environment-provider.tsx b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/onboarding-environment-provider.tsx index 9c3c9a0c72c..04f09c6132f 100644 --- a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/onboarding-environment-provider.tsx +++ b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/onboarding-environment-provider.tsx @@ -17,7 +17,10 @@ import { EnvironmentContext } from "../../[projectId]/(overview)/settings/enviro * but syncs production setting changes to every other environment so new * projects start with consistent config. */ -export const OnboardingEnvironmentSettingsProvider = ({ children }: PropsWithChildren) => { +export const OnboardingEnvironmentSettingsProvider = ({ + children, + isActive, +}: PropsWithChildren<{ isActive: boolean }>) => { const { environments } = useProjectData(); const prodEnvId = useMemo( @@ -45,25 +48,7 @@ export const OnboardingEnvironmentSettingsProvider = ({ children }: PropsWithChi { enabled: Boolean(prodEnvId) }, ); - const hasInitializedRegionsRef = useRef(false); - - // Settings are empty initially so we set all of them by default for the user. - // Later he can change it in the settings - useEffect(() => { - if (!settings || !availableRegions || hasInitializedRegionsRef.current) { - return; - } - hasInitializedRegionsRef.current = true; - if (Object.keys(settings.regionConfig).length > 0) { - return; - } - collection.environmentSettings.update(settings.environmentId, (draft) => { - for (const region of availableRegions) { - draft.regionConfig[region] = 1; - } - }); - }, [settings, availableRegions]); - + useInitializeSettings(settings, availableRegions, isActive); useSyncSettingsToOtherEnvironments(settings, otherEnvIds); if (!settings) { @@ -77,6 +62,56 @@ export const OnboardingEnvironmentSettingsProvider = ({ children }: PropsWithChi ); }; +// Settings are empty initially so we set all of them by default for the user. +// Later they can change it in the settings. +function useInitializeSettings( + settings: EnvironmentSettings | undefined, + availableRegions: string[] | undefined, + isActive: boolean, +) { + const hasInitializedRef = useRef(false); + + useEffect(() => { + if (!settings || !availableRegions || !isActive) { + return; + } + if (hasInitializedRef.current) { + return; + } + hasInitializedRef.current = true; + + collection.environmentSettings.update( + settings.environmentId, + { metadata: { silent: true } }, + (draft) => { + if (!draft.dockerfile) { + draft.dockerfile = "Dockerfile"; + } + if (!draft.dockerContext) { + draft.dockerContext = "."; + } + if (!draft.port) { + draft.port = 8080; + } + if (!draft.cpuMillicores) { + draft.cpuMillicores = 256; + } + if (!draft.memoryMib) { + draft.memoryMib = 256; + } + if (!draft.shutdownSignal) { + draft.shutdownSignal = "SIGTERM"; + } + if (Object.keys(draft.regionConfig).length === 0) { + for (const region of availableRegions) { + draft.regionConfig[region] = 1; + } + } + }, + ); + }, [settings, availableRegions, isActive]); +} + function useSyncSettingsToOtherEnvironments( settings: EnvironmentSettings | undefined, otherEnvIds: string[], diff --git a/web/apps/dashboard/app/(app)/integrations/github/callback/page.tsx b/web/apps/dashboard/app/(app)/integrations/github/callback/page.tsx index b3731dfc111..3d07a231aec 100644 --- a/web/apps/dashboard/app/(app)/integrations/github/callback/page.tsx +++ b/web/apps/dashboard/app/(app)/integrations/github/callback/page.tsx @@ -21,7 +21,6 @@ export default function Page() { const mutation = trpc.github.registerInstallation.useMutation({ onSuccess: (data) => { - toast.success("GitHub App installed"); if (data.returnTo === "settings") { router.replace(`/${data.workspaceSlug}/projects/${data.projectId}/settings`); } else { diff --git a/web/apps/dashboard/lib/collections/deploy/environment-settings.ts b/web/apps/dashboard/lib/collections/deploy/environment-settings.ts index 938ee11bb85..a8a2380e1ff 100644 --- a/web/apps/dashboard/lib/collections/deploy/environment-settings.ts +++ b/web/apps/dashboard/lib/collections/deploy/environment-settings.ts @@ -86,7 +86,8 @@ export const environmentSettings = createCollection id: "environmentSettings", onUpdate: async ({ transaction }) => { const { original, modified } = transaction.mutations[0]; - await dispatchSettingsMutations(original, modified); + const silent = transaction.metadata?.silent === true; + await dispatchSettingsMutations(original, modified, silent); }, }), ); @@ -244,6 +245,7 @@ export function buildSettingsMutations( async function dispatchSettingsMutations( original: EnvironmentSettings, modified: EnvironmentSettings, + silent = false, ): Promise { const mutations = buildSettingsMutations(original.environmentId, original, modified); @@ -252,13 +254,15 @@ async function dispatchSettingsMutations( } const allMutations = Promise.all(mutations); - toast.promise(allMutations, { - loading: "Saving settings...", - success: "Settings updated", - error: (err) => ({ - message: "Failed to update settings", - description: err instanceof Error ? err.message : "An unexpected error occurred", - }), - }); + if (!silent) { + toast.promise(allMutations, { + loading: "Saving settings...", + success: "Settings updated", + error: (err) => ({ + message: "Failed to update settings", + description: err instanceof Error ? err.message : "An unexpected error occurred", + }), + }); + } await allMutations; } From d8e477b0f34ca59bbab74776231cc50329a9c2de Mon Sep 17 00:00:00 2001 From: ogzhanolguncu Date: Wed, 4 Mar 2026 15:47:53 +0300 Subject: [PATCH 04/12] fix: styles and prevent displaying tooltip --- .../deployments/components/table/deployments-list.tsx | 6 +++--- .../settings/components/shared/form-setting-card.tsx | 7 ++++++- .../[projectId]/components/status-indicator.tsx | 3 ++- .../projects/new/steps/select-repo/language-icon.tsx | 6 +++--- .../projects/new/steps/select-repo/repo-list-item.tsx | 10 +++++----- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/components/table/deployments-list.tsx b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/components/table/deployments-list.tsx index 7376a03ca75..728e7419528 100644 --- a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/components/table/deployments-list.tsx +++ b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/components/table/deployments-list.tsx @@ -68,9 +68,9 @@ export const DeploymentsList = () => { const isLive = liveDeploymentId === deployment.id; const iconContainer = ; return ( -
+
-
{iconContainer}
+
{iconContainer}
{ {shortenId(deployment.id)}
{isLive ? ( -
+
{project?.isRolledBack ? ( ) : ( diff --git a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/shared/form-setting-card.tsx b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/shared/form-setting-card.tsx index 39a29151747..433e60097cc 100644 --- a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/shared/form-setting-card.tsx +++ b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/settings/components/shared/form-setting-card.tsx @@ -61,7 +61,12 @@ export const FormSettingCard = ({ } }} > -
+
{children}
{!autoSave && ( diff --git a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/components/status-indicator.tsx b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/components/status-indicator.tsx index 053f9984df7..26c70fd46c2 100644 --- a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/components/status-indicator.tsx +++ b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/components/status-indicator.tsx @@ -43,11 +43,12 @@ export function StatusIndicator({ return (
{ return Icon ? (
- +
) : (
-
- +
+
); diff --git a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/select-repo/repo-list-item.tsx b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/select-repo/repo-list-item.tsx index 412be2f3036..4f9092aa678 100644 --- a/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/select-repo/repo-list-item.tsx +++ b/web/apps/dashboard/app/(app)/[workspaceSlug]/projects/new/steps/select-repo/repo-list-item.tsx @@ -55,8 +55,8 @@ export const RepoListItem = ({ return (
-
-
+
+
{repoName}
@@ -76,20 +76,20 @@ export const RepoListItem = ({ )}
-
+
{isLoading ? (
) : (