improve resource type picker

This commit is contained in:
miloschwartz
2026-08-13 15:52:33 -04:00
parent ed71c90d73
commit f9ff3a5715
6 changed files with 349 additions and 202 deletions
@@ -12,9 +12,9 @@ import {
} from "@app/components/Settings";
import HeaderTitle from "@app/components/SettingsSectionTitle";
import {
OptionSelect,
type OptionSelectOption
} from "@app/components/OptionSelect";
DescribedSelect,
type DescribedSelectOption
} from "@app/components/DescribedSelect";
import DomainPicker from "@app/components/DomainPicker";
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
import { Button } from "@app/components/ui/button";
@@ -147,24 +147,35 @@ export default function CreatePrivateResourcePage() {
const authDaemonMode = form.watch("authDaemonMode");
const isNativeSsh = mode === "ssh" && authDaemonMode === "native";
const modeOptions: OptionSelectOption<PrivateResourceMode>[] = [
{ value: "host", label: t("createInternalResourceDialogModeHost") },
{ value: "cidr", label: t("createInternalResourceDialogModeCidr") },
const modeOptions: DescribedSelectOption<PrivateResourceMode>[] = [
{
value: "host",
title: t("createInternalResourceDialogModeHost"),
description: t("privateResourceTypeHostDescription")
},
{
value: "cidr",
title: t("createInternalResourceDialogModeCidr"),
description: t("privateResourceTypeCidrDescription")
},
...(!disableEnterpriseFeatures
? [
{
value: "http" as const,
label: t("createInternalResourceDialogModeHttp")
title: t("createInternalResourceDialogModeHttp"),
description: t("privateResourceTypeHttpDescription")
},
{
value: "ssh" as const,
label: t("createInternalResourceDialogModeSsh")
title: t("createInternalResourceDialogModeSsh"),
description: t("privateResourceTypeSshDescription")
}
]
: []),
{
value: "inference" as const,
label: t("createInternalResourceDialogModeInference")
title: t("createInternalResourceDialogModeInference"),
description: t("resourceTypeInferenceDescription")
}
];
@@ -260,6 +271,110 @@ export default function CreatePrivateResourcePage() {
<SettingsSectionBody>
<SettingsSectionForm variant="half">
<SettingsFormGrid>
<SettingsFormCell span="half">
<FormField
control={form.control}
name="mode"
render={({ field }) => (
<FormItem>
<FormLabel>
{t("type")}
</FormLabel>
<FormControl>
<DescribedSelect<PrivateResourceMode>
options={
modeOptions
}
value={field.value}
onChange={(
newMode
) => {
field.onChange(
newMode
);
if (
newMode ===
"ssh"
) {
form.setValue(
"authDaemonMode",
"native"
);
form.setValue(
"standardDaemonLocation",
"site"
);
form.setValue(
"destination",
null
);
form.setValue(
"destinationPort",
null
);
} else if (
newMode ===
"http"
) {
form.setValue(
"destinationPort",
443
);
} else if (
newMode ===
"inference"
) {
form.setValue(
"siteIds",
[]
);
setSelectedSites(
[]
);
form.setValue(
"destination",
null
);
form.setValue(
"destinationPort",
null
);
form.setValue(
"providerIds",
[]
);
setSelectedProviders(
[]
);
} else {
form.setValue(
"destinationPort",
null
);
}
}}
searchPlaceholder={t(
"resourceTypeSearch"
)}
emptyMessage={t(
"resourceTypeNotFound"
)}
placeholder={t(
"noneSelected"
)}
/>
</FormControl>
<FormMessage />
<FormDescription>
{t(
"privateResourceTypeDescription"
)}
</FormDescription>
</FormItem>
)}
/>
</SettingsFormCell>
<SettingsFormCell span="half">
<FormField
control={form.control}
@@ -283,91 +398,6 @@ export default function CreatePrivateResourcePage() {
/>
</SettingsFormCell>
<SettingsFormCell span="full">
<FormField
control={form.control}
name="mode"
render={({ field }) => (
<FormItem>
<FormLabel>
{t("type")}
</FormLabel>
<OptionSelect<PrivateResourceMode>
options={modeOptions}
value={field.value}
onChange={(newMode) => {
field.onChange(
newMode
);
if (
newMode ===
"ssh"
) {
form.setValue(
"authDaemonMode",
"native"
);
form.setValue(
"standardDaemonLocation",
"site"
);
form.setValue(
"destination",
null
);
form.setValue(
"destinationPort",
null
);
} else if (
newMode ===
"http"
) {
form.setValue(
"destinationPort",
443
);
} else if (
newMode ===
"inference"
) {
form.setValue(
"siteIds",
[]
);
setSelectedSites(
[]
);
form.setValue(
"destination",
null
);
form.setValue(
"destinationPort",
null
);
form.setValue(
"providerIds",
[]
);
setSelectedProviders(
[]
);
} else {
form.setValue(
"destinationPort",
null
);
}
}}
cols={4}
/>
<FormMessage />
</FormItem>
)}
/>
</SettingsFormCell>
{(mode === "http" ||
mode === "inference") && (
<SettingsFormCell span="full">
@@ -18,9 +18,9 @@ import {
} from "@app/components/Settings";
import HeaderTitle from "@app/components/SettingsSectionTitle";
import {
OptionSelect,
type OptionSelectOption
} from "@app/components/OptionSelect";
DescribedSelect,
type DescribedSelectOption
} from "@app/components/DescribedSelect";
import {
StrategySelect,
type StrategyOption
@@ -775,26 +775,45 @@ export default function Page() {
}
];
let typeLabels: Partial<Record<NewResourceType, string>> = {
http: "HTTP",
inference: t("createInternalResourceDialogModeInference"),
tcp: "TCP",
udp: "UDP"
const typeMeta: Record<
NewResourceType,
{ title: string; description: string }
> = {
http: {
title: t("createInternalResourceDialogModeHttp"),
description: t("resourceTypeHttpDescription")
},
inference: {
title: t("createInternalResourceDialogModeInference"),
description: t("resourceTypeInferenceDescription")
},
ssh: {
title: t("createInternalResourceDialogModeSsh"),
description: t("resourceTypeSshDescription")
},
rdp: {
title: t("rdpTitle"),
description: t("resourceTypeRdpDescription")
},
vnc: {
title: t("vncTitle"),
description: t("resourceTypeVncDescription")
},
tcp: {
title: t("createInternalResourceDialogTcp"),
description: t("resourceTypeTcpDescription")
},
udp: {
title: t("createInternalResourceDialogUdp"),
description: t("resourceTypeUdpDescription")
}
};
if (enterpriseModesAllowed) {
typeLabels = {
...typeLabels,
ssh: "SSH",
rdp: "RDP",
vnc: "VNC"
};
}
const typeOptions: OptionSelectOption<NewResourceType>[] =
const typeOptions: DescribedSelectOption<NewResourceType>[] =
availableTypes.map((type) => ({
value: type,
label: typeLabels[type] ?? type.toUpperCase()
title: typeMeta[type].title,
description: typeMeta[type].description
}));
return (
@@ -831,6 +850,35 @@ export default function Page() {
<SettingsSectionBody>
<SettingsSectionForm variant="half">
<SettingsFormGrid>
<SettingsFormCell span="half">
<div className="grid gap-2">
<Label>
{t("type")}
</Label>
<DescribedSelect<NewResourceType>
options={typeOptions}
value={resourceType}
onChange={
setResourceType
}
searchPlaceholder={t(
"resourceTypeSearch"
)}
emptyMessage={t(
"resourceTypeNotFound"
)}
placeholder={t(
"noneSelected"
)}
/>
<p className="text-muted-foreground text-sm">
{t(
"resourceTypeDescription"
)}
</p>
</div>
</SettingsFormCell>
<SettingsFormCell span="half">
<Form {...baseForm}>
<form
@@ -876,27 +924,6 @@ export default function Page() {
</Form>
</SettingsFormCell>
<SettingsFormCell span="full">
<div className="space-y-2">
<p className="text-sm font-medium">
{t("type")}
</p>
<OptionSelect<NewResourceType>
options={typeOptions}
value={resourceType}
onChange={
setResourceType
}
cols={6}
/>
<p className="text-sm text-muted-foreground">
{t(
"resourceTypeDescription"
)}
</p>
</div>
</SettingsFormCell>
{isHttpResource && (
<SettingsFormCell span="full">
<Form {...httpForm}>