Show/allow unicode domain name

This commit is contained in:
Pallavi
2025-08-08 23:08:08 +05:30
parent f37eda4739
commit 78f464f6ca
23 changed files with 333 additions and 173 deletions

View File

@@ -43,6 +43,7 @@ import {
validateByDomainType,
isValidSubdomainStructure
} from "@/lib/subdomain-utils";
import { toUnicode } from "punycode";
type OrganizationDomain = {
domainId: string;
@@ -126,6 +127,7 @@ export default function DomainPicker2({
)
.map((domain) => ({
...domain,
baseDomain: toUnicode(domain.baseDomain),
type: domain.type as "ns" | "cname" | "wildcard"
}));
setOrganizationDomains(domains);
@@ -406,6 +408,12 @@ export default function DomainPicker2({
const hasMoreProvided =
sortedAvailableOptions.length > providedDomainsShown;
const isValidDomainCharacter = (char: string) => {
// Allow Unicode letters, numbers, hyphens, and periods
return /[\p{L}\p{N}.-]/u.test(char);
};
return (
<div className="space-y-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
@@ -424,8 +432,8 @@ export default function DomainPicker2({
showProvidedDomainSearch
? ""
: showSubdomainInput
? ""
: t("domainPickerNotAvailableForCname")
? ""
: t("domainPickerNotAvailableForCname")
}
disabled={
!showSubdomainInput && !showProvidedDomainSearch
@@ -436,10 +444,16 @@ export default function DomainPicker2({
"border-red-500 focus:border-red-500"
)}
onChange={(e) => {
const rawInput = e.target.value;
const validInput = rawInput
.split("")
.filter((char) => isValidDomainCharacter(char))
.join("");
if (showProvidedDomainSearch) {
handleProvidedDomainInputChange(e.target.value);
handleProvidedDomainInputChange(validInput);
} else {
handleSubdomainChange(e.target.value);
handleSubdomainChange(validInput);
}
}}
/>
@@ -448,7 +462,6 @@ export default function DomainPicker2({
This subdomain contains invalid characters or structure. It will be sanitized automatically when you save.
</p>
)}
{showSubdomainInput && !subdomainInput && (
<p className="text-sm text-muted-foreground">
{t("domainPickerEnterSubdomainOrLeaveBlank")}
@@ -474,7 +487,7 @@ export default function DomainPicker2({
{selectedBaseDomain ? (
<div className="flex items-center space-x-2 min-w-0 flex-1">
{selectedBaseDomain.type ===
"organization" ? null : (
"organization" ? null : (
<Zap className="h-4 w-4 flex-shrink-0" />
)}
<span className="truncate">
@@ -568,67 +581,67 @@ export default function DomainPicker2({
</CommandGroup>
{(build === "saas" ||
build === "enterprise") && (
<CommandSeparator className="my-2" />
)}
<CommandSeparator className="my-2" />
)}
</>
)}
{(build === "saas" ||
build === "enterprise") && (
<CommandGroup
heading={
build === "enterprise"
? t(
"domainPickerProvidedDomains"
)
: t("domainPickerFreeDomains")
}
className="py-2"
>
<CommandList>
<CommandItem
key="provided-search"
onSelect={() =>
handleBaseDomainSelect({
id: "provided-search",
domain:
build ===
"enterprise"
<CommandGroup
heading={
build === "enterprise"
? t(
"domainPickerProvidedDomains"
)
: t("domainPickerFreeDomains")
}
className="py-2"
>
<CommandList>
<CommandItem
key="provided-search"
onSelect={() =>
handleBaseDomainSelect({
id: "provided-search",
domain:
build ===
"enterprise"
? "Provided Domain"
: "Free Provided Domain",
type: "provided-search"
})
}
className="mx-2 rounded-md"
>
<div className="flex items-center justify-center w-8 h-8 rounded-lg bg-primary/10 mr-3">
<Zap className="h-4 w-4 text-primary" />
</div>
<div className="flex flex-col flex-1 min-w-0">
<span className="font-medium truncate">
{build === "enterprise"
? "Provided Domain"
: "Free Provided Domain",
type: "provided-search"
})
}
className="mx-2 rounded-md"
>
<div className="flex items-center justify-center w-8 h-8 rounded-lg bg-primary/10 mr-3">
<Zap className="h-4 w-4 text-primary" />
</div>
<div className="flex flex-col flex-1 min-w-0">
<span className="font-medium truncate">
{build === "enterprise"
? "Provided Domain"
: "Free Provided Domain"}
</span>
<span className="text-xs text-muted-foreground">
{t(
"domainPickerSearchForAvailableDomains"
: "Free Provided Domain"}
</span>
<span className="text-xs text-muted-foreground">
{t(
"domainPickerSearchForAvailableDomains"
)}
</span>
</div>
<Check
className={cn(
"h-4 w-4 text-primary",
selectedBaseDomain?.id ===
"provided-search"
? "opacity-100"
: "opacity-0"
)}
</span>
</div>
<Check
className={cn(
"h-4 w-4 text-primary",
selectedBaseDomain?.id ===
"provided-search"
? "opacity-100"
: "opacity-0"
)}
/>
</CommandItem>
</CommandList>
</CommandGroup>
)}
/>
</CommandItem>
</CommandList>
</CommandGroup>
)}
</Command>
</PopoverContent>
</Popover>
@@ -684,7 +697,7 @@ export default function DomainPicker2({
htmlFor={option.domainNamespaceId}
data-state={
selectedProvidedDomain?.domainNamespaceId ===
option.domainNamespaceId
option.domainNamespaceId
? "checked"
: "unchecked"
}
@@ -764,4 +777,4 @@ function debounce<T extends (...args: any[]) => any>(
func(...args);
}, wait);
};
}
}