From fbd3802e468c5220b67edcd9d880968b4e9fb2ba Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Wed, 10 Dec 2025 21:15:26 +0100 Subject: [PATCH 1/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Update=20domain=20pick?= =?UTF-8?q?er=20component=20to=20accept=20default=20values?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/DomainPicker.tsx | 252 ++++++++++++++------------------ src/lib/queries.ts | 12 ++ 2 files changed, 118 insertions(+), 146 deletions(-) diff --git a/src/components/DomainPicker.tsx b/src/components/DomainPicker.tsx index 2d17e39f..8fc6d583 100644 --- a/src/components/DomainPicker.tsx +++ b/src/components/DomainPicker.tsx @@ -1,8 +1,6 @@ "use client"; -import { useState, useEffect, useCallback } from "react"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; +import { Alert, AlertDescription } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import { Command, @@ -13,45 +11,40 @@ import { CommandList, CommandSeparator } from "@/components/ui/command"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import { - AlertCircle, - CheckCircle2, - Building2, - Zap, - Check, - ChevronsUpDown, - ArrowUpDown -} from "lucide-react"; -import { Alert, AlertDescription } from "@/components/ui/alert"; -import { createApiClient, formatAxiosError } from "@/lib/api"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { useEnvContext } from "@/hooks/useEnvContext"; import { toast } from "@/hooks/useToast"; -import { ListDomainsResponse } from "@server/routers/domain/listDomains"; -import { CheckDomainAvailabilityResponse } from "@server/routers/domain/types"; -import { AxiosResponse } from "axios"; +import { createApiClient } from "@/lib/api"; import { cn } from "@/lib/cn"; -import { useTranslations } from "next-intl"; -import { build } from "@server/build"; -import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { - sanitizeInputRaw, finalizeSubdomainSanitize, - validateByDomainType, - isValidSubdomainStructure + isValidSubdomainStructure, + sanitizeInputRaw, + validateByDomainType } from "@/lib/subdomain-utils"; +import { orgQueries } from "@app/lib/queries"; +import { build } from "@server/build"; +import { CheckDomainAvailabilityResponse } from "@server/routers/domain/types"; +import { useQuery } from "@tanstack/react-query"; +import { AxiosResponse } from "axios"; +import { + AlertCircle, + Building2, + Check, + CheckCircle2, + ChevronsUpDown, + Zap +} from "lucide-react"; +import { useTranslations } from "next-intl"; import { toUnicode } from "punycode"; - -type OrganizationDomain = { - domainId: string; - baseDomain: string; - verified: boolean; - type: "ns" | "cname" | "wildcard"; -}; +import { useCallback, useEffect, useMemo, useState } from "react"; type AvailableOption = { domainNamespaceId: string; @@ -69,7 +62,7 @@ type DomainOption = { domainNamespaceId?: string; }; -interface DomainPicker2Props { +interface DomainPickerProps { orgId: string; onDomainChange?: (domainInfo: { domainId: string; @@ -81,116 +74,115 @@ interface DomainPicker2Props { }) => void; cols?: number; hideFreeDomain?: boolean; + defaultSubdomain?: string; + defaultBaseDomain?: string; } -export default function DomainPicker2({ +export default function DomainPicker({ orgId, onDomainChange, cols = 2, - hideFreeDomain = false -}: DomainPicker2Props) { + hideFreeDomain = false, + defaultSubdomain, + defaultBaseDomain +}: DomainPickerProps) { const { env } = useEnvContext(); const api = createApiClient({ env }); const t = useTranslations(); + const { data = [], isLoading: loadingDomains } = useQuery( + orgQueries.domains({ orgId }) + ); + + console.log({ defaultSubdomain, defaultBaseDomain }); + if (!env.flags.usePangolinDns) { hideFreeDomain = true; } - const [subdomainInput, setSubdomainInput] = useState(""); + const [subdomainInput, setSubdomainInput] = useState( + defaultSubdomain ?? "" + ); const [selectedBaseDomain, setSelectedBaseDomain] = useState(null); const [availableOptions, setAvailableOptions] = useState( [] ); - const [organizationDomains, setOrganizationDomains] = useState< - OrganizationDomain[] - >([]); - const [loadingDomains, setLoadingDomains] = useState(false); + + // memoized to prevent reruning the effect that selects the initial domain indefinitely + // removing this will break and cause an infinite rerender + const organizationDomains = useMemo(() => { + return data + .filter( + (domain) => + domain.type === "ns" || + domain.type === "cname" || + domain.type === "wildcard" + ) + .map((domain) => ({ + ...domain, + baseDomain: toUnicode(domain.baseDomain), + type: domain.type as "ns" | "cname" | "wildcard" + })); + }, [data]); + const [open, setOpen] = useState(false); // Provided domain search states const [userInput, setUserInput] = useState(""); const [isChecking, setIsChecking] = useState(false); - const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc"); const [providedDomainsShown, setProvidedDomainsShown] = useState(3); const [selectedProvidedDomain, setSelectedProvidedDomain] = useState(null); useEffect(() => { - const loadOrganizationDomains = async () => { - setLoadingDomains(true); - try { - const response = await api.get< - AxiosResponse - >(`/org/${orgId}/domains`); - if (response.status === 200) { - const domains = response.data.data.domains - .filter( - (domain) => - domain.type === "ns" || - domain.type === "cname" || - domain.type === "wildcard" - ) - .map((domain) => ({ - ...domain, - baseDomain: toUnicode(domain.baseDomain), - type: domain.type as "ns" | "cname" | "wildcard" - })); - setOrganizationDomains(domains); + if (!loadingDomains) { + if (organizationDomains.length > 0) { + // Select the first organization domain or the one provided from props + const firstOrgDomain = + organizationDomains.find( + (domain) => domain.baseDomain === defaultBaseDomain + ) ?? organizationDomains[0]; + const domainOption: DomainOption = { + id: `org-${firstOrgDomain.domainId}`, + domain: firstOrgDomain.baseDomain, + type: "organization", + verified: firstOrgDomain.verified, + domainType: firstOrgDomain.type, + domainId: firstOrgDomain.domainId + }; + setSelectedBaseDomain(domainOption); - // Auto-select first available domain - if (domains.length > 0) { - // Select the first organization domain - const firstOrgDomain = domains[0]; - const domainOption: DomainOption = { - id: `org-${firstOrgDomain.domainId}`, - domain: firstOrgDomain.baseDomain, - type: "organization", - verified: firstOrgDomain.verified, - domainType: firstOrgDomain.type, - domainId: firstOrgDomain.domainId - }; - setSelectedBaseDomain(domainOption); - - onDomainChange?.({ - domainId: firstOrgDomain.domainId, - type: "organization", - subdomain: undefined, - fullDomain: firstOrgDomain.baseDomain, - baseDomain: firstOrgDomain.baseDomain - }); - } else if ( - (build === "saas" || build === "enterprise") && - !hideFreeDomain - ) { - // If no organization domains, select the provided domain option - const domainOptionText = - build === "enterprise" - ? t("domainPickerProvidedDomain") - : t("domainPickerFreeProvidedDomain"); - const freeDomainOption: DomainOption = { - id: "provided-search", - domain: domainOptionText, - type: "provided-search" - }; - setSelectedBaseDomain(freeDomainOption); - } - } - } catch (error) { - console.error("Failed to load organization domains:", error); - toast({ - variant: "destructive", - title: t("domainPickerError"), - description: t("domainPickerErrorLoadDomains") + onDomainChange?.({ + domainId: firstOrgDomain.domainId, + type: "organization", + subdomain: undefined, + fullDomain: firstOrgDomain.baseDomain, + baseDomain: firstOrgDomain.baseDomain }); - } finally { - setLoadingDomains(false); + } else if ( + (build === "saas" || build === "enterprise") && + !hideFreeDomain + ) { + // If no organization domains, select the provided domain option + const domainOptionText = + build === "enterprise" + ? t("domainPickerProvidedDomain") + : t("domainPickerFreeProvidedDomain"); + const freeDomainOption: DomainOption = { + id: "provided-search", + domain: domainOptionText, + type: "provided-search" + }; + setSelectedBaseDomain(freeDomainOption); } - }; - - loadOrganizationDomains(); - }, [orgId, api, hideFreeDomain]); + } + }, [ + hideFreeDomain, + loadingDomains, + organizationDomains, + defaultBaseDomain + ]); const checkAvailability = useCallback( async (input: string) => { @@ -256,37 +248,6 @@ export default function DomainPicker2({ } }, [userInput, debouncedCheckAvailability, selectedBaseDomain]); - const generateDropdownOptions = (): DomainOption[] => { - const options: DomainOption[] = []; - - organizationDomains.forEach((orgDomain) => { - options.push({ - id: `org-${orgDomain.domainId}`, - domain: orgDomain.baseDomain, - type: "organization", - verified: orgDomain.verified, - domainType: orgDomain.type, - domainId: orgDomain.domainId - }); - }); - - if ((build === "saas" || build === "enterprise") && !hideFreeDomain) { - const domainOptionText = - build === "enterprise" - ? t("domainPickerProvidedDomain") - : t("domainPickerFreeProvidedDomain"); - options.push({ - id: "provided-search", - domain: domainOptionText, - type: "provided-search" - }); - } - - return options; - }; - - const dropdownOptions = generateDropdownOptions(); - const finalizeSubdomain = (sub: string, base: DomainOption): string => { const sanitized = finalizeSubdomainSanitize(sub); @@ -440,8 +401,7 @@ export default function DomainPicker2({ selectedBaseDomain?.type === "provided-search"; const sortedAvailableOptions = [...availableOptions].sort((a, b) => { - const comparison = a.fullDomain.localeCompare(b.fullDomain); - return sortOrder === "asc" ? comparison : -comparison; + return a.fullDomain.localeCompare(b.fullDomain); }); const displayedProvidedOptions = sortedAvailableOptions.slice( @@ -518,16 +478,16 @@ export default function DomainPicker2({ className="w-full justify-between" > {selectedBaseDomain ? ( -
+
{selectedBaseDomain.type === "organization" ? null : ( - + )} {selectedBaseDomain.domain} {selectedBaseDomain.verified && ( - + )}
) : ( diff --git a/src/lib/queries.ts b/src/lib/queries.ts index 2a19f3f9..73e80df8 100644 --- a/src/lib/queries.ts +++ b/src/lib/queries.ts @@ -16,6 +16,7 @@ import { remote } from "./api"; import { durationToMs } from "./durationToMs"; import type { QueryRequestAnalyticsResponse } from "@server/routers/auditLogs"; import type { ListResourceNamesResponse } from "@server/routers/resource"; +import type { ListDomainsResponse } from "@server/routers/domain"; export type ProductUpdate = { link: string | null; @@ -139,6 +140,17 @@ export const orgQueries = { >(`/org/${orgId}/sites`, { signal }); return res.data.data.sites; } + }), + + domains: ({ orgId }: { orgId: string }) => + queryOptions({ + queryKey: ["ORG", orgId, "DOMAINS"] as const, + queryFn: async ({ signal, meta }) => { + const res = await meta!.api.get< + AxiosResponse + >(`/org/${orgId}/domains`, { signal }); + return res.data.data.domains; + } }) }; From de684b212fbe3cbca259781f2d09a429b506261b Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Wed, 10 Dec 2025 21:26:46 +0100 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=94=87=20remove=20console.log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/DomainPicker.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/DomainPicker.tsx b/src/components/DomainPicker.tsx index 8fc6d583..625b566e 100644 --- a/src/components/DomainPicker.tsx +++ b/src/components/DomainPicker.tsx @@ -94,8 +94,6 @@ export default function DomainPicker({ orgQueries.domains({ orgId }) ); - console.log({ defaultSubdomain, defaultBaseDomain }); - if (!env.flags.usePangolinDns) { hideFreeDomain = true; } From aab0471b6b23e0205feb5144e0ea8a1070ead0c7 Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Wed, 10 Dec 2025 21:26:55 +0100 Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20fix=20typescript=20?= =?UTF-8?q?errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/lib/rebuildClientAssociations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/lib/rebuildClientAssociations.ts b/server/lib/rebuildClientAssociations.ts index 549dbffe..e0867dc5 100644 --- a/server/lib/rebuildClientAssociations.ts +++ b/server/lib/rebuildClientAssociations.ts @@ -1085,7 +1085,7 @@ async function handleMessagesForClientSites( continue; } - await holepunchSiteAdd( + await initPeerAddHandshake( // this will kick off the add peer process for the client client.clientId, { From 6fc54bcc9e20a337ee856a8a1bdb72521d338621 Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Thu, 11 Dec 2025 22:51:02 +0100 Subject: [PATCH 4/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20set=20default=20value?= =?UTF-8?q?=20on=20domain=20picker=20modal=20in=20proxy=20resource=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/proxy/[niceId]/general/page.tsx | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx b/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx index fa9a6976..7d4db07c 100644 --- a/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx @@ -16,7 +16,7 @@ import { import { Input } from "@/components/ui/input"; import { useResourceContext } from "@app/hooks/useResourceContext"; import { ListSitesResponse } from "@server/routers/site"; -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { AxiosResponse } from "axios"; import { useParams, useRouter } from "next/navigation"; import { useForm } from "react-hook-form"; @@ -90,6 +90,25 @@ export default function GeneralForm() { const [resourceFullDomain, setResourceFullDomain] = useState( `${resource.ssl ? "https" : "http"}://${toUnicode(resource.fullDomain || "")}` ); + + const [defaultSubdomain, defaultBaseDomain] = useMemo(() => { + const resourceUrl = new URL(resourceFullDomain); + const domain = resourceUrl.hostname; + + const allDomainParts = domain.split("."); + let sub = undefined; + let base = domain; + + if (allDomainParts.length >= 3) { + // 3 parts: [subdomain, domain, tld] + const [first, ...rest] = allDomainParts; + sub = first; + base = rest.join("."); + } + + return [sub, base]; + }, [resourceFullDomain]); + const [selectedDomain, setSelectedDomain] = useState<{ domainId: string; subdomain?: string; @@ -488,6 +507,8 @@ export default function GeneralForm() { { const selected = { domainId: res.domainId, From e02fa7c14889cd3ba5bcc6b19214384a9fcf65cb Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Wed, 17 Dec 2025 00:52:12 +0100 Subject: [PATCH 5/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20pass=20the=20default?= =?UTF-8?q?=20domainId=20instead=20of=20the=20base=20domain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/proxy/[niceId]/general/page.tsx | 22 ++----------------- src/components/DomainPicker.tsx | 17 ++++++-------- 2 files changed, 9 insertions(+), 30 deletions(-) diff --git a/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx b/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx index 7d4db07c..ed1080f3 100644 --- a/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx @@ -91,24 +91,6 @@ export default function GeneralForm() { `${resource.ssl ? "https" : "http"}://${toUnicode(resource.fullDomain || "")}` ); - const [defaultSubdomain, defaultBaseDomain] = useMemo(() => { - const resourceUrl = new URL(resourceFullDomain); - const domain = resourceUrl.hostname; - - const allDomainParts = domain.split("."); - let sub = undefined; - let base = domain; - - if (allDomainParts.length >= 3) { - // 3 parts: [subdomain, domain, tld] - const [first, ...rest] = allDomainParts; - sub = first; - base = rest.join("."); - } - - return [sub, base]; - }, [resourceFullDomain]); - const [selectedDomain, setSelectedDomain] = useState<{ domainId: string; subdomain?: string; @@ -507,8 +489,8 @@ export default function GeneralForm() { { const selected = { domainId: res.domainId, diff --git a/src/components/DomainPicker.tsx b/src/components/DomainPicker.tsx index 625b566e..5e883add 100644 --- a/src/components/DomainPicker.tsx +++ b/src/components/DomainPicker.tsx @@ -74,8 +74,9 @@ interface DomainPickerProps { }) => void; cols?: number; hideFreeDomain?: boolean; - defaultSubdomain?: string; - defaultBaseDomain?: string; + defaultFullDomain?: string | null; + defaultSubdomain?: string | null; + defaultDomainId?: string | null; } export default function DomainPicker({ @@ -84,7 +85,8 @@ export default function DomainPicker({ cols = 2, hideFreeDomain = false, defaultSubdomain, - defaultBaseDomain + defaultFullDomain, + defaultDomainId }: DomainPickerProps) { const { env } = useEnvContext(); const api = createApiClient({ env }); @@ -139,7 +141,7 @@ export default function DomainPicker({ // Select the first organization domain or the one provided from props const firstOrgDomain = organizationDomains.find( - (domain) => domain.baseDomain === defaultBaseDomain + (domain) => domain.domainId === defaultDomainId ) ?? organizationDomains[0]; const domainOption: DomainOption = { id: `org-${firstOrgDomain.domainId}`, @@ -175,12 +177,7 @@ export default function DomainPicker({ setSelectedBaseDomain(freeDomainOption); } } - }, [ - hideFreeDomain, - loadingDomains, - organizationDomains, - defaultBaseDomain - ]); + }, [hideFreeDomain, loadingDomains, organizationDomains, defaultDomainId]); const checkAvailability = useCallback( async (input: string) => { From c98d61a8fb937c24ec5bf1a6abe3d49d83dd5a23 Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Wed, 17 Dec 2025 02:36:29 +0100 Subject: [PATCH 6/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20pass=20default=20value?= =?UTF-8?q?=20to=20domain=20picker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/proxy/[niceId]/general/page.tsx | 13 ++++++++++--- src/components/DomainPicker.tsx | 17 ++++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx b/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx index 9b2c120e..8d3ccab5 100644 --- a/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx @@ -226,7 +226,8 @@ export default function GeneralForm() { niceId: data.niceId, subdomain: data.subdomain, fullDomain: updated.fullDomain, - proxyPort: data.proxyPort + proxyPort: data.proxyPort, + domainId: data.domainId // ...(!resource.http && { // enableProxy: data.enableProxy // }) @@ -489,8 +490,14 @@ export default function GeneralForm() { { const selected = { domainId: res.domainId, diff --git a/src/components/DomainPicker.tsx b/src/components/DomainPicker.tsx index 5e883add..36e76b2b 100644 --- a/src/components/DomainPicker.tsx +++ b/src/components/DomainPicker.tsx @@ -143,6 +143,7 @@ export default function DomainPicker({ organizationDomains.find( (domain) => domain.domainId === defaultDomainId ) ?? organizationDomains[0]; + const domainOption: DomainOption = { id: `org-${firstOrgDomain.domainId}`, domain: firstOrgDomain.baseDomain, @@ -156,7 +157,10 @@ export default function DomainPicker({ onDomainChange?.({ domainId: firstOrgDomain.domainId, type: "organization", - subdomain: undefined, + subdomain: + firstOrgDomain.type !== "cname" + ? defaultSubdomain || undefined + : undefined, fullDomain: firstOrgDomain.baseDomain, baseDomain: firstOrgDomain.baseDomain }); @@ -177,7 +181,13 @@ export default function DomainPicker({ setSelectedBaseDomain(freeDomainOption); } } - }, [hideFreeDomain, loadingDomains, organizationDomains, defaultDomainId]); + }, [ + loadingDomains, + organizationDomains, + defaultSubdomain, + hideFreeDomain, + defaultDomainId + ]); const checkAvailability = useCallback( async (input: string) => { @@ -354,7 +364,8 @@ export default function DomainPicker({ domainNamespaceId: option.domainNamespaceId, type: option.type === "provided-search" ? "provided" : "organization", - subdomain: sub || undefined, + subdomain: + option.domainType !== "cname" ? sub || undefined : undefined, fullDomain, baseDomain: option.domain }); From b133593ea2e87b07e7ed7fad333391705c1cad58 Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Wed, 17 Dec 2025 04:57:16 +0100 Subject: [PATCH 7/9] =?UTF-8?q?=F0=9F=9A=B8=20now=20the=20domain=20picker?= =?UTF-8?q?=20is=20deterministic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/proxy/[niceId]/general/page.tsx | 14 +- src/components/DomainPicker.tsx | 171 ++++++++++-------- 2 files changed, 109 insertions(+), 76 deletions(-) diff --git a/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx b/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx index 8d3ccab5..e846ec6c 100644 --- a/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx @@ -91,8 +91,14 @@ export default function GeneralForm() { `${resource.ssl ? "https" : "http"}://${toUnicode(resource.fullDomain || "")}` ); + const resourceFullDomainName = useMemo(() => { + const url = new URL(resourceFullDomain); + return url.hostname; + }, [resourceFullDomain]); + const [selectedDomain, setSelectedDomain] = useState<{ domainId: string; + domainNamespaceId?: string; subdomain?: string; fullDomain: string; baseDomain: string; @@ -491,19 +497,21 @@ export default function GeneralForm() { orgId={orgId as string} cols={1} defaultSubdomain={ - selectedDomain?.subdomain ?? + form.getValues("subdomain") ?? resource.subdomain } defaultDomainId={ - selectedDomain?.domainId ?? + form.getValues("domainId") ?? resource.domainId } + defaultFullDomain={resourceFullDomainName} onDomainChange={(res) => { const selected = { domainId: res.domainId, subdomain: res.subdomain, fullDomain: res.fullDomain, - baseDomain: res.baseDomain + baseDomain: res.baseDomain, + domainNamespaceId: res.domainNamespaceId }; setSelectedDomain(selected); }} diff --git a/src/components/DomainPicker.tsx b/src/components/DomainPicker.tsx index 36e76b2b..ba29c029 100644 --- a/src/components/DomainPicker.tsx +++ b/src/components/DomainPicker.tsx @@ -103,6 +103,7 @@ export default function DomainPicker({ const [subdomainInput, setSubdomainInput] = useState( defaultSubdomain ?? "" ); + const [selectedBaseDomain, setSelectedBaseDomain] = useState(null); const [availableOptions, setAvailableOptions] = useState( @@ -129,7 +130,7 @@ export default function DomainPicker({ const [open, setOpen] = useState(false); // Provided domain search states - const [userInput, setUserInput] = useState(""); + const [userInput, setUserInput] = useState(defaultSubdomain ?? ""); const [isChecking, setIsChecking] = useState(false); const [providedDomainsShown, setProvidedDomainsShown] = useState(3); const [selectedProvidedDomain, setSelectedProvidedDomain] = @@ -137,49 +138,60 @@ export default function DomainPicker({ useEffect(() => { if (!loadingDomains) { + let domainOptionToSelect: DomainOption | null = null; if (organizationDomains.length > 0) { // Select the first organization domain or the one provided from props - const firstOrgDomain = - organizationDomains.find( - (domain) => domain.domainId === defaultDomainId - ) ?? organizationDomains[0]; + let firstOrExistingDomain = organizationDomains.find( + (domain) => domain.domainId === defaultDomainId + ); + // if no default Domain + if (!defaultDomainId) { + firstOrExistingDomain = organizationDomains[0]; + } - const domainOption: DomainOption = { - id: `org-${firstOrgDomain.domainId}`, - domain: firstOrgDomain.baseDomain, - type: "organization", - verified: firstOrgDomain.verified, - domainType: firstOrgDomain.type, - domainId: firstOrgDomain.domainId - }; - setSelectedBaseDomain(domainOption); + if (firstOrExistingDomain) { + domainOptionToSelect = { + id: `org-${firstOrExistingDomain.domainId}`, + domain: firstOrExistingDomain.baseDomain, + type: "organization", + verified: firstOrExistingDomain.verified, + domainType: firstOrExistingDomain.type, + domainId: firstOrExistingDomain.domainId + }; - onDomainChange?.({ - domainId: firstOrgDomain.domainId, - type: "organization", - subdomain: - firstOrgDomain.type !== "cname" - ? defaultSubdomain || undefined - : undefined, - fullDomain: firstOrgDomain.baseDomain, - baseDomain: firstOrgDomain.baseDomain - }); - } else if ( - (build === "saas" || build === "enterprise") && - !hideFreeDomain + onDomainChange?.({ + domainId: firstOrExistingDomain.domainId, + type: "organization", + subdomain: + firstOrExistingDomain.type !== "cname" + ? defaultSubdomain || undefined + : undefined, + fullDomain: firstOrExistingDomain.baseDomain, + baseDomain: firstOrExistingDomain.baseDomain + }); + } + } + + if ( + !domainOptionToSelect && + build !== "oss" && + !hideFreeDomain && + defaultDomainId !== undefined ) { // If no organization domains, select the provided domain option const domainOptionText = build === "enterprise" ? t("domainPickerProvidedDomain") : t("domainPickerFreeProvidedDomain"); - const freeDomainOption: DomainOption = { + // free domain option + domainOptionToSelect = { id: "provided-search", domain: domainOptionText, type: "provided-search" }; - setSelectedBaseDomain(freeDomainOption); } + + setSelectedBaseDomain(domainOptionToSelect); } }, [ loadingDomains, @@ -349,6 +361,9 @@ export default function DomainPicker({ setSelectedProvidedDomain(null); } + console.log({ + setSelectedBaseDomain: option + }); setSelectedBaseDomain(option); setOpen(false); @@ -414,6 +429,15 @@ export default function DomainPicker({ 0, providedDomainsShown ); + console.log({ + displayedProvidedOptions + }); + + const selectedDomainNamespaceId = + selectedProvidedDomain?.domainNamespaceId ?? + displayedProvidedOptions.find( + (opt) => opt.fullDomain === defaultFullDomain + )?.domainNamespaceId; const hasMoreProvided = sortedAvailableOptions.length > providedDomainsShown; @@ -699,10 +723,8 @@ export default function DomainPicker({ {!isChecking && sortedAvailableOptions.length > 0 && (
{ const option = displayedProvidedOptions.find( @@ -715,47 +737,50 @@ export default function DomainPicker({ }} className={`grid gap-2 grid-cols-1 sm:grid-cols-${cols}`} > - {displayedProvidedOptions.map((option) => ( -