feat(ui): add conditional server dropdown with Dokploy default option - Add IS_CLOUD flag support for server selection dropdown - Show "Dokploy" as default option in self-hosted environments - Hide dropdown when no remote servers exist - Add conditional placeholder text and server count display - Handle "dokploy" value in form submission (converts to undefined) - Apply changes to all relevant components: add-application, add-compose, add-template, add-database, add-certificate, and AI step-one Resolves #1852

This commit is contained in:
HarikrishnanD
2025-09-04 13:54:19 +05:30
parent 52e21dab4e
commit 46f1af3bb3
6 changed files with 104 additions and 66 deletions
@@ -76,6 +76,10 @@ export const AddApplication = ({ projectId, projectName }: Props) => {
const { data: servers } = api.server.withSSHKey.useQuery(); const { data: servers } = api.server.withSSHKey.useQuery();
const hasServers = servers && servers.length > 0; const hasServers = servers && servers.length > 0;
// Show dropdown logic based on cloud environment
// Cloud: show only if there are remote servers (no Dokploy option)
// Self-hosted: show only if there are remote servers (Dokploy is default, hide if no remote servers)
const shouldShowServerDropdown = hasServers;
const { mutateAsync, isLoading, error, isError } = const { mutateAsync, isLoading, error, isError } =
api.application.create.useMutation(); api.application.create.useMutation();
@@ -157,7 +161,7 @@ export const AddApplication = ({ projectId, projectName }: Props) => {
</FormItem> </FormItem>
)} )}
/> />
{hasServers && ( {shouldShowServerDropdown && (
<FormField <FormField
control={form.control} control={form.control}
name="serverId" name="serverId"
@@ -186,21 +190,23 @@ export const AddApplication = ({ projectId, projectName }: Props) => {
<Select <Select
onValueChange={field.onChange} onValueChange={field.onChange}
defaultValue={field.value || "dokploy"} defaultValue={field.value || (!isCloud ? "dokploy" : undefined)}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Dokploy" /> <SelectValue placeholder={!isCloud ? "Dokploy" : "Select a Server"} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
<SelectItem value="dokploy"> {!isCloud && (
<span className="flex items-center gap-2 justify-between w-full"> <SelectItem value="dokploy">
<span>Dokploy</span> <span className="flex items-center gap-2 justify-between w-full">
<span className="text-muted-foreground text-xs self-center"> <span>Dokploy</span>
Default <span className="text-muted-foreground text-xs self-center">
Default
</span>
</span> </span>
</span> </SelectItem>
</SelectItem> )}
{servers?.map((server) => ( {servers?.map((server) => (
<SelectItem <SelectItem
key={server.serverId} key={server.serverId}
@@ -215,7 +221,7 @@ export const AddApplication = ({ projectId, projectName }: Props) => {
</SelectItem> </SelectItem>
))} ))}
<SelectLabel> <SelectLabel>
Servers ({servers?.length + 1}) Servers ({servers?.length + (!isCloud ? 1 : 0)})
</SelectLabel> </SelectLabel>
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>
@@ -79,6 +79,10 @@ export const AddCompose = ({ projectId, projectName }: Props) => {
api.compose.create.useMutation(); api.compose.create.useMutation();
const hasServers = servers && servers.length > 0; const hasServers = servers && servers.length > 0;
// Show dropdown logic based on cloud environment
// Cloud: show only if there are remote servers (no Dokploy option)
// Self-hosted: show only if there are remote servers (Dokploy is default, hide if no remote servers)
const shouldShowServerDropdown = hasServers;
const form = useForm<AddCompose>({ const form = useForm<AddCompose>({
defaultValues: { defaultValues: {
@@ -165,7 +169,7 @@ export const AddCompose = ({ projectId, projectName }: Props) => {
)} )}
/> />
</div> </div>
{hasServers && ( {shouldShowServerDropdown && (
<FormField <FormField
control={form.control} control={form.control}
name="serverId" name="serverId"
@@ -194,21 +198,23 @@ export const AddCompose = ({ projectId, projectName }: Props) => {
<Select <Select
onValueChange={field.onChange} onValueChange={field.onChange}
defaultValue={field.value || "dokploy"} defaultValue={field.value || (!isCloud ? "dokploy" : undefined)}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Dokploy" /> <SelectValue placeholder={!isCloud ? "Dokploy" : "Select a Server"} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
<SelectItem value="dokploy"> {!isCloud && (
<span className="flex items-center gap-2 justify-between w-full"> <SelectItem value="dokploy">
<span>Dokploy</span> <span className="flex items-center gap-2 justify-between w-full">
<span className="text-muted-foreground text-xs self-center"> <span>Dokploy</span>
Default <span className="text-muted-foreground text-xs self-center">
Default
</span>
</span> </span>
</span> </SelectItem>
</SelectItem> )}
{servers?.map((server) => ( {servers?.map((server) => (
<SelectItem <SelectItem
key={server.serverId} key={server.serverId}
@@ -223,7 +229,7 @@ export const AddCompose = ({ projectId, projectName }: Props) => {
</SelectItem> </SelectItem>
))} ))}
<SelectLabel> <SelectLabel>
Servers ({servers?.length + 1}) Servers ({servers?.length + (!isCloud ? 1 : 0)})
</SelectLabel> </SelectLabel>
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>
@@ -178,6 +178,7 @@ export const AddDatabase = ({ projectId, projectName }: Props) => {
const utils = api.useUtils(); const utils = api.useUtils();
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const slug = slugify(projectName); const slug = slugify(projectName);
const { data: isCloud } = api.settings.isCloud.useQuery();
const { data: servers } = api.server.withSSHKey.useQuery(); const { data: servers } = api.server.withSSHKey.useQuery();
const postgresMutation = api.postgres.create.useMutation(); const postgresMutation = api.postgres.create.useMutation();
const mongoMutation = api.mongo.create.useMutation(); const mongoMutation = api.mongo.create.useMutation();
@@ -186,6 +187,10 @@ export const AddDatabase = ({ projectId, projectName }: Props) => {
const mysqlMutation = api.mysql.create.useMutation(); const mysqlMutation = api.mysql.create.useMutation();
const hasServers = servers && servers.length > 0; const hasServers = servers && servers.length > 0;
// Show dropdown logic based on cloud environment
// Cloud: show only if there are remote servers (no Dokploy option)
// Self-hosted: show only if there are remote servers (Dokploy is default, hide if no remote servers)
const shouldShowServerDropdown = hasServers;
const form = useForm<AddDatabase>({ const form = useForm<AddDatabase>({
defaultValues: { defaultValues: {
@@ -398,7 +403,7 @@ export const AddDatabase = ({ projectId, projectName }: Props) => {
</FormItem> </FormItem>
)} )}
/> />
{hasServers && ( {shouldShowServerDropdown && (
<FormField <FormField
control={form.control} control={form.control}
name="serverId" name="serverId"
@@ -407,21 +412,23 @@ export const AddDatabase = ({ projectId, projectName }: Props) => {
<FormLabel>Select a Server</FormLabel> <FormLabel>Select a Server</FormLabel>
<Select <Select
onValueChange={field.onChange} onValueChange={field.onChange}
defaultValue={field.value || "dokploy"} defaultValue={field.value || (!isCloud ? "dokploy" : undefined)}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Dokploy" /> <SelectValue placeholder={!isCloud ? "Dokploy" : "Select a Server"} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
<SelectItem value="dokploy"> {!isCloud && (
<span className="flex items-center gap-2 justify-between w-full"> <SelectItem value="dokploy">
<span>Dokploy</span> <span className="flex items-center gap-2 justify-between w-full">
<span className="text-muted-foreground text-xs self-center"> <span>Dokploy</span>
Default <span className="text-muted-foreground text-xs self-center">
Default
</span>
</span> </span>
</span> </SelectItem>
</SelectItem> )}
{servers?.map((server) => ( {servers?.map((server) => (
<SelectItem <SelectItem
key={server.serverId} key={server.serverId}
@@ -431,7 +438,7 @@ export const AddDatabase = ({ projectId, projectName }: Props) => {
</SelectItem> </SelectItem>
))} ))}
<SelectLabel> <SelectLabel>
Servers ({servers?.length + 1}) Servers ({servers?.length + (!isCloud ? 1 : 0)})
</SelectLabel> </SelectLabel>
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>
@@ -138,6 +138,10 @@ export const AddTemplate = ({ projectId, baseUrl }: Props) => {
}) || []; }) || [];
const hasServers = servers && servers.length > 0; const hasServers = servers && servers.length > 0;
// Show dropdown logic based on cloud environment
// Cloud: show only if there are remote servers (no Dokploy option)
// Self-hosted: show only if there are remote servers (Dokploy is default, hide if no remote servers)
const shouldShowServerDropdown = hasServers;
return ( return (
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
@@ -427,7 +431,7 @@ export const AddTemplate = ({ projectId, baseUrl }: Props) => {
project. project.
</AlertDialogDescription> </AlertDialogDescription>
{hasServers && ( {shouldShowServerDropdown && (
<div> <div>
<TooltipProvider delayDuration={0}> <TooltipProvider delayDuration={0}>
<Tooltip> <Tooltip>
@@ -456,21 +460,23 @@ export const AddTemplate = ({ projectId, baseUrl }: Props) => {
onValueChange={(e) => { onValueChange={(e) => {
setServerId(e); setServerId(e);
}} }}
defaultValue="dokploy" defaultValue={!isCloud ? "dokploy" : undefined}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Dokploy" /> <SelectValue placeholder={!isCloud ? "Dokploy" : "Select a Server"} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
<SelectItem value="dokploy"> {!isCloud && (
<span className="flex items-center gap-2 justify-between w-full"> <SelectItem value="dokploy">
<span>Dokploy</span> <span className="flex items-center gap-2 justify-between w-full">
<span className="text-muted-foreground text-xs self-center"> <span>Dokploy</span>
Default <span className="text-muted-foreground text-xs self-center">
Default
</span>
</span> </span>
</span> </SelectItem>
</SelectItem> )}
{servers?.map((server) => ( {servers?.map((server) => (
<SelectItem <SelectItem
key={server.serverId} key={server.serverId}
@@ -485,7 +491,7 @@ export const AddTemplate = ({ projectId, baseUrl }: Props) => {
</SelectItem> </SelectItem>
))} ))}
<SelectLabel> <SelectLabel>
Servers ({servers?.length + 1}) Servers ({servers?.length + (!isCloud ? 1 : 0)})
</SelectLabel> </SelectLabel>
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>
@@ -25,7 +25,12 @@ const examples = [
export const StepOne = ({ setTemplateInfo, templateInfo }: any) => { export const StepOne = ({ setTemplateInfo, templateInfo }: any) => {
// Get servers from the API // Get servers from the API
const { data: servers } = api.server.withSSHKey.useQuery(); const { data: servers } = api.server.withSSHKey.useQuery();
const { data: isCloud } = api.settings.isCloud.useQuery();
const hasServers = servers && servers.length > 0; const hasServers = servers && servers.length > 0;
// Show dropdown logic based on cloud environment
// Cloud: show only if there are remote servers (no Dokploy option)
// Self-hosted: show only if there are remote servers (Dokploy is default, hide if no remote servers)
const shouldShowServerDropdown = hasServers;
const handleExampleClick = (example: string) => { const handleExampleClick = (example: string) => {
setTemplateInfo({ ...templateInfo, userInput: example }); setTemplateInfo({ ...templateInfo, userInput: example });
@@ -48,13 +53,13 @@ export const StepOne = ({ setTemplateInfo, templateInfo }: any) => {
/> />
</div> </div>
{hasServers && ( {shouldShowServerDropdown && (
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="server-deploy"> <Label htmlFor="server-deploy">
Select the server where you want to deploy (optional) Select the server where you want to deploy (optional)
</Label> </Label>
<Select <Select
value={templateInfo.server?.serverId || "dokploy"} value={templateInfo.server?.serverId || (!isCloud ? "dokploy" : undefined)}
onValueChange={(value) => { onValueChange={(value) => {
if (value === "dokploy") { if (value === "dokploy") {
setTemplateInfo({ setTemplateInfo({
@@ -73,24 +78,26 @@ export const StepOne = ({ setTemplateInfo, templateInfo }: any) => {
}} }}
> >
<SelectTrigger className="w-full"> <SelectTrigger className="w-full">
<SelectValue placeholder="Dokploy" /> <SelectValue placeholder={!isCloud ? "Dokploy" : "Select a Server"} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
<SelectItem value="dokploy"> {!isCloud && (
<span className="flex items-center gap-2 justify-between w-full"> <SelectItem value="dokploy">
<span>Dokploy</span> <span className="flex items-center gap-2 justify-between w-full">
<span className="text-muted-foreground text-xs self-center"> <span>Dokploy</span>
Default <span className="text-muted-foreground text-xs self-center">
Default
</span>
</span> </span>
</span> </SelectItem>
</SelectItem> )}
{servers?.map((server) => ( {servers?.map((server) => (
<SelectItem key={server.serverId} value={server.serverId}> <SelectItem key={server.serverId} value={server.serverId}>
{server.name} {server.name}
</SelectItem> </SelectItem>
))} ))}
<SelectLabel>Servers ({servers?.length + 1})</SelectLabel> <SelectLabel>Servers ({servers?.length + (!isCloud ? 1 : 0)})</SelectLabel>
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
@@ -66,6 +66,10 @@ export const AddCertificate = () => {
api.certificates.create.useMutation(); api.certificates.create.useMutation();
const { data: servers } = api.server.withSSHKey.useQuery(); const { data: servers } = api.server.withSSHKey.useQuery();
const hasServers = servers && servers.length > 0; const hasServers = servers && servers.length > 0;
// Show dropdown logic based on cloud environment
// Cloud: show only if there are remote servers (no Dokploy option)
// Self-hosted: show only if there are remote servers (Dokploy is default, hide if no remote servers)
const shouldShowServerDropdown = hasServers;
const form = useForm<AddCertificate>({ const form = useForm<AddCertificate>({
defaultValues: { defaultValues: {
@@ -175,7 +179,7 @@ export const AddCertificate = () => {
</FormItem> </FormItem>
)} )}
/> />
{hasServers && ( {shouldShowServerDropdown && (
<FormField <FormField
control={form.control} control={form.control}
name="serverId" name="serverId"
@@ -194,21 +198,23 @@ export const AddCertificate = () => {
<Select <Select
onValueChange={field.onChange} onValueChange={field.onChange}
defaultValue={field.value || "dokploy"} defaultValue={field.value || (!isCloud ? "dokploy" : undefined)}
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Dokploy" /> <SelectValue placeholder={!isCloud ? "Dokploy" : "Select a Server"} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
<SelectItem value="dokploy"> {!isCloud && (
<span className="flex items-center gap-2 justify-between w-full"> <SelectItem value="dokploy">
<span>Dokploy</span> <span className="flex items-center gap-2 justify-between w-full">
<span className="text-muted-foreground text-xs self-center"> <span>Dokploy</span>
Default <span className="text-muted-foreground text-xs self-center">
Default
</span>
</span> </span>
</span> </SelectItem>
</SelectItem> )}
{servers?.map((server) => ( {servers?.map((server) => (
<SelectItem <SelectItem
key={server.serverId} key={server.serverId}
@@ -223,7 +229,7 @@ export const AddCertificate = () => {
</SelectItem> </SelectItem>
))} ))}
<SelectLabel> <SelectLabel>
Servers ({servers?.length + 1}) Servers ({servers?.length + (!isCloud ? 1 : 0)})
</SelectLabel> </SelectLabel>
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>