Skip to content
Merged
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 @@ -15,7 +15,7 @@ import {
import { Controller, useForm } from "react-hook-form";
import { useProjectData } from "../../../../data-provider";
import { useEnvironmentSettings } from "../../../environment-provider";
import { FormSettingCard } from "../../shared/form-setting-card";
import { FormSettingCard, resolveSaveState } from "../../shared/form-setting-card";
import { CustomDomainRow } from "./custom-domain-row";
import { type CustomDomainFormValues, customDomainSchema } from "./schema";

Expand Down Expand Up @@ -90,6 +90,11 @@ const CustomDomainSettings: React.FC<CustomDomainSettingsProps> = ({
reset({ environmentId: values.environmentId, domain: "" });
};

const saveState = resolveSaveState([
[isSubmitting, { status: "saving" }],
[!isValid, { status: "disabled" }],
]);

const displayValue =
customDomains.length === 0 ? null : (
<div className="space-x-1">
Expand All @@ -107,8 +112,7 @@ const CustomDomainSettings: React.FC<CustomDomainSettingsProps> = ({
description="Serve your deployment from your own domain name"
displayValue={displayValue}
onSubmit={handleSubmit(onSubmit)}
canSave={isValid && !isSubmitting}
isSaving={isSubmitting}
saveState={saveState}
>
<div className="flex flex-col gap-3 w-full">
<div className="flex items-center gap-3">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useMemo } from "react";
import { useFieldArray, useForm } from "react-hook-form";
import { useProjectData } from "../../../../data-provider";
import { useEnvironmentSettings } from "../../../environment-provider";
import { FormSettingCard } from "../../shared/form-setting-card";
import { FormSettingCard, resolveSaveState } from "../../shared/form-setting-card";
import { EnvVarRow } from "./env-var-row";
import { type EnvVarsFormValues, createEmptyRow, envVarsSchema } from "./schema";
import { useDecryptedValues } from "./use-decrypted-values";
Expand Down Expand Up @@ -136,6 +136,13 @@ const EnvVarsForm = ({
]);
};

const saveState = resolveSaveState([
[isSubmitting, { status: "saving" }],
[isDecrypting, { status: "disabled", reason: "Decrypting values…" }],
[!isValid, { status: "disabled" }],
[!isDirty, { status: "disabled", reason: "No changes to save" }],
]);

const varCount = defaultValues.envVars.filter((v) => v.key !== "").length;
const displayValue =
varCount === 0 ? null : (
Expand All @@ -152,8 +159,7 @@ const EnvVarsForm = ({
description="Set environment variables available at runtime. Changes apply on next deploy."
displayValue={displayValue}
onSubmit={handleSubmit(onSubmit)}
canSave={isValid && !isSubmitting && !isDecrypting && isDirty}
isSaving={isSubmitting}
saveState={saveState}
ref={ref}
className={cn("relative", isDragging && "bg-primary/5")}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FormInput } from "@unkey/ui";
import { useForm, useWatch } from "react-hook-form";
import { z } from "zod";
import { useEnvironmentSettings } from "../../environment-provider";
import { FormSettingCard } from "../shared/form-setting-card";
import { FormSettingCard, resolveSaveState } from "../shared/form-setting-card";

const dockerfileSchema = z.object({
dockerfile: z.string().min(1, "Dockerfile path is required"),
Expand All @@ -28,6 +28,12 @@ export const Dockerfile = () => {

const currentDockerfile = useWatch({ control, name: "dockerfile" });

const saveState = resolveSaveState([
[isSubmitting, { status: "saving" }],
[!isValid, { status: "disabled" }],
[currentDockerfile === defaultValue, { status: "disabled", reason: "No changes to save" }],
]);

const onSubmit = async (values: z.infer<typeof dockerfileSchema>) => {
collection.environmentSettings.update(environmentId, (draft) => {
draft.dockerfile = values.dockerfile;
Expand All @@ -41,8 +47,7 @@ export const Dockerfile = () => {
description="Dockerfile location used for docker build. (e.g., services/api/Dockerfile)"
displayValue={defaultValue}
onSubmit={handleSubmit(onSubmit)}
canSave={isValid && !isSubmitting && currentDockerfile !== defaultValue}
isSaving={isSubmitting}
saveState={saveState}
>
<FormInput
required
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FormInput } from "@unkey/ui";
import { useForm, useWatch } from "react-hook-form";
import { z } from "zod";
import { useEnvironmentSettings } from "../../environment-provider";
import { FormSettingCard } from "../shared/form-setting-card";
import { FormSettingCard, resolveSaveState } from "../shared/form-setting-card";

const rootDirectorySchema = z.object({
dockerContext: z.string(),
Expand All @@ -28,6 +28,12 @@ export const RootDirectory = () => {

const currentDockerContext = useWatch({ control, name: "dockerContext" });

const saveState = resolveSaveState([
[isSubmitting, { status: "saving" }],
[!isValid, { status: "disabled" }],
[currentDockerContext === defaultValue, { status: "disabled", reason: "No changes to save" }],
]);

const onSubmit = async (values: z.infer<typeof rootDirectorySchema>) => {
collection.environmentSettings.update(environmentId, (draft) => {
draft.dockerContext = values.dockerContext;
Expand All @@ -41,8 +47,7 @@ export const RootDirectory = () => {
description="Build context directory. All COPY/ADD commands are relative to this path. (e.g., services/api)"
displayValue={defaultValue || "."}
onSubmit={handleSubmit(onSubmit)}
canSave={isValid && !isSubmitting && currentDockerContext !== defaultValue}
isSaving={isSubmitting}
saveState={saveState}
>
<FormInput
label="Root directory"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useEffect } from "react";
import { useForm, useWatch } from "react-hook-form";
import { z } from "zod";
import { useEnvironmentSettings } from "../../environment-provider";
import { FormSettingCard } from "../shared/form-setting-card";
import { FormSettingCard, resolveSaveState } from "../shared/form-setting-card";

const commandSchema = z.object({
command: z.string(),
Expand Down Expand Up @@ -40,6 +40,12 @@ export const Command = () => {
const currentCommand = useWatch({ control, name: "command" });
const hasChanges = currentCommand !== defaultCommand;

const saveState = resolveSaveState([
[isSubmitting, { status: "saving" }],
[!isValid, { status: "disabled" }],
[!hasChanges, { status: "disabled", reason: "No changes to save" }],
]);

const onSubmit = async (values: CommandFormValues) => {
const trimmed = values.command.trim();
const command = trimmed === "" ? [] : trimmed.split(/\s+/).filter(Boolean);
Expand All @@ -63,8 +69,7 @@ export const Command = () => {
) : null
}
onSubmit={handleSubmit(onSubmit)}
canSave={isValid && !isSubmitting && hasChanges}
isSaving={isSubmitting}
saveState={saveState}
>
<FormTextarea
label="Command"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useEffect } from "react";
import { useForm, useWatch } from "react-hook-form";
import { z } from "zod";
import { useEnvironmentSettings } from "../../environment-provider";
import { FormSettingCard } from "../shared/form-setting-card";
import { FormSettingCard, resolveSaveState } from "../shared/form-setting-card";
import { SettingDescription } from "../shared/setting-description";
import { indexToValue, valueToIndex } from "../shared/slider-utils";

Expand Down Expand Up @@ -61,6 +61,12 @@ export const Cpu = () => {
const hasChanges = currentCpu !== defaultCpu;
const currentIndex = valueToIndex(CPU_OPTIONS, currentCpu);

const saveState = resolveSaveState([
[isSubmitting, { status: "saving" }],
[!isValid, { status: "disabled" }],
[!hasChanges, { status: "disabled", reason: "No changes to save" }],
]);

return (
<FormSettingCard
icon={<Bolt className="text-gray-12" iconSize="xl-medium" />}
Expand All @@ -76,8 +82,7 @@ export const Cpu = () => {
);
})()}
onSubmit={handleSubmit(onSubmit)}
canSave={isValid && !isSubmitting && hasChanges}
isSaving={isSubmitting}
saveState={saveState}
>
<div className="flex flex-col">
<span className="text-gray-11 text-[13px]">CPU per instance</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { useEffect } from "react";
import { Controller, useForm, useWatch } from "react-hook-form";
import { useEnvironmentSettings } from "../../../environment-provider";
import { FormSettingCard } from "../../shared/form-setting-card";
import { FormSettingCard, resolveSaveState } from "../../shared/form-setting-card";
import { MethodBadge } from "./method-badge";
import { HTTP_METHODS, type HealthcheckFormValues, healthcheckSchema } from "./schema";
import { intervalToSeconds, secondsToInterval } from "./utils";
Expand Down Expand Up @@ -72,6 +72,12 @@ export const Healthcheck = () => {
currentPath !== defaultValues.path ||
currentInterval !== defaultValues.interval;

const saveState = resolveSaveState([
[isSubmitting, { status: "saving" }],
[!isValid, { status: "disabled" }],
[!hasChanges, { status: "disabled", reason: "No changes to save" }],
]);

return (
<FormSettingCard
icon={<HeartPulse className="text-gray-12" iconSize="xl-medium" />}
Expand All @@ -87,8 +93,7 @@ export const Healthcheck = () => {
) : null
}
onSubmit={handleSubmit(onSubmit)}
canSave={isValid && !isSubmitting && hasChanges}
isSaving={isSubmitting}
saveState={saveState}
>
<div className="flex flex-col gap-3 w-[520px]">
{/* TODO: multi-check when API supports
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useForm, useWatch } from "react-hook-form";
import { z } from "zod";
import { RegionFlag } from "../../../../components/region-flag";
import { useEnvironmentSettings } from "../../environment-provider";
import { FormSettingCard } from "../shared/form-setting-card";
import { FormSettingCard, resolveSaveState } from "../shared/form-setting-card";
import { SettingDescription } from "../shared/setting-description";

const instancesSchema = z.object({
Expand Down Expand Up @@ -55,6 +55,17 @@ export const Instances = () => {
};

const hasChanges = currentInstances !== defaultInstances;
const hasRegions = Object.keys(regionConfig).length > 0;

const saveState = resolveSaveState([
[isSubmitting, { status: "saving" }],
[
!hasRegions,
{ status: "disabled", reason: "Select at least one region before setting instance count" },
],
[!isValid, { status: "disabled" }],
[!hasChanges, { status: "disabled", reason: "No changes to save" }],
]);

return (
<FormSettingCard
Expand All @@ -70,8 +81,7 @@ export const Instances = () => {
</div>
}
onSubmit={handleSubmit(onSubmit)}
canSave={isValid && !isSubmitting && hasChanges}
isSaving={isSubmitting}
saveState={saveState}
>
<div className="flex flex-col">
<span className="text-gray-11 text-[13px]">Instances per region</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useEffect } from "react";
import { useForm, useWatch } from "react-hook-form";
import { z } from "zod";
import { useEnvironmentSettings } from "../../environment-provider";
import { FormSettingCard } from "../shared/form-setting-card";
import { FormSettingCard, resolveSaveState } from "../shared/form-setting-card";
import { SettingDescription } from "../shared/setting-description";
import { indexToValue, valueToIndex } from "../shared/slider-utils";

Expand Down Expand Up @@ -61,6 +61,12 @@ export const Memory = () => {
const hasChanges = currentMemory !== defaultMemory;
const currentIndex = valueToIndex(MEMORY_OPTIONS, currentMemory);

const saveState = resolveSaveState([
[isSubmitting, { status: "saving" }],
[!isValid, { status: "disabled" }],
[!hasChanges, { status: "disabled", reason: "No changes to save" }],
]);

return (
<FormSettingCard
icon={<ScanCode className="text-gray-12" iconSize="xl-medium" />}
Expand All @@ -76,8 +82,7 @@ export const Memory = () => {
);
})()}
onSubmit={handleSubmit(onSubmit)}
canSave={isValid && !isSubmitting && hasChanges}
isSaving={isSubmitting}
saveState={saveState}
>
<div className="flex flex-col">
<span className="text-gray-11 text-[13px]">Memory per instance</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FormInput } from "@unkey/ui";
import { useForm, useWatch } from "react-hook-form";
import { z } from "zod";
import { useEnvironmentSettings } from "../../environment-provider";
import { FormSettingCard } from "../shared/form-setting-card";
import { FormSettingCard, resolveSaveState } from "../shared/form-setting-card";

const portSchema = z.object({
port: z.number().int().min(2000).max(54000),
Expand All @@ -28,6 +28,12 @@ export const Port = () => {

const currentPort = useWatch({ control, name: "port" });

const saveState = resolveSaveState([
[isSubmitting, { status: "saving" }],
[!isValid, { status: "disabled" }],
[currentPort === defaultValue, { status: "disabled", reason: "No changes to save" }],
]);

const onSubmit = async (values: z.infer<typeof portSchema>) => {
collection.environmentSettings.update(environmentId, (draft) => {
draft.port = values.port;
Expand All @@ -41,8 +47,7 @@ export const Port = () => {
description="Port your application listens on"
displayValue={String(defaultValue)}
onSubmit={handleSubmit(onSubmit)}
canSave={isValid && !isSubmitting && currentPort !== defaultValue}
isSaving={isSubmitting}
saveState={saveState}
>
<FormInput
required
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { useForm, useWatch } from "react-hook-form";
import { z } from "zod";
import { RegionFlag } from "../../../../components/region-flag";
import { useEnvironmentSettings } from "../../environment-provider";
import { FormSettingCard } from "../shared/form-setting-card";
import { FormSettingCard, resolveSaveState } from "../shared/form-setting-card";

const regionsSchema = z.object({
regions: z.array(z.string()).min(1, "Select at least one region"),
Expand Down Expand Up @@ -99,6 +99,12 @@ const RegionsForm: React.FC<RegionsFormProps> = ({
currentRegions.length !== defaultRegions.length ||
currentRegions.some((r) => !defaultRegions.includes(r));

const saveState = resolveSaveState([
[isSubmitting, { status: "saving" }],
[!isValid, { status: "disabled" }],
[!hasChanges, { status: "disabled", reason: "No changes to save" }],
]);

const displayValue =
defaultRegions.length === 0 ? null : defaultRegions.length <= 2 ? (
<span className="flex items-center gap-1.5">
Expand Down Expand Up @@ -143,8 +149,7 @@ const RegionsForm: React.FC<RegionsFormProps> = ({
description="Geographic regions where your project will run"
displayValue={displayValue}
onSubmit={handleSubmit(onSubmit)}
canSave={isValid && !isSubmitting && hasChanges}
isSaving={isSubmitting}
saveState={saveState}
>
<FormCombobox
label="Regions"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Gauge } from "@unkey/icons";
import { Slider } from "@unkey/ui";
import { useForm, useWatch } from "react-hook-form";
import { z } from "zod";
import { FormSettingCard } from "../shared/form-setting-card";
import { FormSettingCard, resolveSaveState } from "../shared/form-setting-card";
import { SettingDescription } from "../shared/setting-description";

const scalingSchema = z
Expand Down Expand Up @@ -47,6 +47,11 @@ export const Scaling = () => {
currentMax !== DEFAULT_VALUES.maxInstances ||
currentCpuThreshold !== DEFAULT_VALUES.cpuThreshold;

const saveState = resolveSaveState([
[!isValid, { status: "disabled" }],
[!hasChanges, { status: "disabled", reason: "No changes to save" }],
]);

return (
<FormSettingCard
icon={<Gauge className="text-gray-12" iconSize="xl-medium" />}
Expand All @@ -64,8 +69,7 @@ export const Scaling = () => {
</div>
}
onSubmit={(e) => e.preventDefault()}
canSave={isValid && hasChanges}
isSaving={false}
saveState={saveState}
>
<div className="flex flex-col gap-4">
<div className="flex flex-col">
Expand Down
Loading