import { cn } from "@app/lib/cn"; import type { DockerState } from "@app/lib/docker"; import { parseHostTarget } from "@app/lib/parseHostTarget"; import { orgQueries } from "@app/lib/queries"; import { CaretSortIcon } from "@radix-ui/react-icons"; import type { ListSitesResponse } from "@server/routers/site"; import { type ListTargetsResponse } from "@server/routers/target"; import type { ArrayElement } from "@server/types/ArrayElement"; import { useQuery } from "@tanstack/react-query"; import { CheckIcon } from "lucide-react"; import { useTranslations } from "next-intl"; import { useMemo, useState } from "react"; import { ContainersSelector } from "./ContainersSelector"; import { Button } from "./ui/button"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "./ui/command"; import { Input } from "./ui/input"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; import { Select, SelectContent, SelectItem, SelectTrigger } from "./ui/select"; type SiteWithUpdateAvailable = ListSitesResponse["sites"][number]; export type LocalTarget = Omit< ArrayElement & { new?: boolean; updated?: boolean; siteType: string | null; }, "protocol" >; export type ResourceTargetAddressItemProps = { getDockerStateForSite: (siteId: number) => DockerState; updateTarget: (targetId: number, data: Partial) => void; orgId: string; proxyTarget: LocalTarget; isHttp: boolean; refreshContainersForSite: (siteId: number) => void; }; export function ResourceTargetAddressItem({ orgId, getDockerStateForSite, updateTarget, proxyTarget, isHttp, refreshContainersForSite }: ResourceTargetAddressItemProps) { const t = useTranslations(); const [siteSearchQuery, setSiteSearchQuery] = useState(""); const { data: sites = [] } = useQuery( orgQueries.sites({ orgId, query: siteSearchQuery, perPage: 10 }) ); const [selectedSite, setSelectedSite] = useState | null>(() => { if ( proxyTarget.siteName && proxyTarget.siteType && proxyTarget.siteId ) { return { name: proxyTarget.siteName, siteId: proxyTarget.siteId, type: proxyTarget.siteType }; } return null; }); const sitesShown = useMemo(() => { const allSites: Array< Pick > = [...sites]; if ( selectedSite !== null && !( allSites.find((site) => site.siteId)?.siteId === selectedSite?.siteId ) ) { allSites.unshift(selectedSite); } return allSites; }, [sites, selectedSite]); const handleContainerSelectForTarget = ( hostname: string, port?: number ) => { updateTarget(proxyTarget.targetId, { ...proxyTarget, ip: hostname, ...(port && { port: port }) }); }; return (
{selectedSite && selectedSite.type === "newt" && ( refreshContainersForSite(selectedSite.siteId) } /> )} setSiteSearchQuery(v)} /> {t("siteNotFound")} {sitesShown.map((site) => ( { updateTarget( proxyTarget.targetId, { siteId: site.siteId, siteType: site.type, siteName: site.name } ); setSelectedSite(site); }} > {site.name} ))} {isHttp && ( )} {isHttp && (
{"://"}
)} { const input = e.target.value.trim(); const hasProtocol = /^(https?|h2c):\/\//.test(input); const hasPort = /:\d+(?:\/|$)/.test(input); if (hasProtocol || hasPort) { const parsed = parseHostTarget(input); if (parsed) { updateTarget(proxyTarget.targetId, { ...proxyTarget, method: hasProtocol ? parsed.protocol : proxyTarget.method, ip: parsed.host, port: hasPort ? parsed.port : proxyTarget.port }); } else { updateTarget(proxyTarget.targetId, { ...proxyTarget, ip: input }); } } else { updateTarget(proxyTarget.targetId, { ...proxyTarget, ip: input }); } }} />
{":"}
{ const value = parseInt(e.target.value, 10); if (!isNaN(value) && value > 0) { updateTarget(proxyTarget.targetId, { ...proxyTarget, port: value }); } else { updateTarget(proxyTarget.targetId, { ...proxyTarget, port: 0 }); } }} />
); }