mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-30 18:35:44 +00:00
♻️ make site selector popover its own component
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
||||
import { Input } from "./ui/input";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger } from "./ui/select";
|
||||
import { SitesSelector } from "./site-selector";
|
||||
|
||||
type SiteWithUpdateAvailable = ListSitesResponse["sites"][number];
|
||||
|
||||
@@ -54,16 +55,6 @@ export function ResourceTargetAddressItem({
|
||||
}: ResourceTargetAddressItemProps) {
|
||||
const t = useTranslations();
|
||||
|
||||
const [siteSearchQuery, setSiteSearchQuery] = useState("");
|
||||
|
||||
const { data: sites = [] } = useQuery(
|
||||
orgQueries.sites({
|
||||
orgId,
|
||||
query: siteSearchQuery,
|
||||
perPage: 10
|
||||
})
|
||||
);
|
||||
|
||||
const [selectedSite, setSelectedSite] = useState<Pick<
|
||||
SiteWithUpdateAvailable,
|
||||
"name" | "siteId" | "type"
|
||||
@@ -82,22 +73,6 @@ export function ResourceTargetAddressItem({
|
||||
return null;
|
||||
});
|
||||
|
||||
const sitesShown = useMemo(() => {
|
||||
const allSites: Array<
|
||||
Pick<SiteWithUpdateAvailable, "name" | "siteId" | "type">
|
||||
> = [...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
|
||||
@@ -150,47 +125,18 @@ export function ResourceTargetAddressItem({
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="p-0 w-45">
|
||||
<Command shouldFilter={false}>
|
||||
<CommandInput
|
||||
placeholder={t("siteSearch")}
|
||||
value={siteSearchQuery}
|
||||
onValueChange={(v) => setSiteSearchQuery(v)}
|
||||
/>
|
||||
<CommandList>
|
||||
<CommandEmpty>{t("siteNotFound")}</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{sitesShown.map((site) => (
|
||||
<CommandItem
|
||||
key={site.siteId}
|
||||
value={`${site.siteId}:${site.name}`}
|
||||
onSelect={() => {
|
||||
updateTarget(
|
||||
proxyTarget.targetId,
|
||||
{
|
||||
siteId: site.siteId,
|
||||
siteType: site.type,
|
||||
siteName: site.name
|
||||
}
|
||||
);
|
||||
|
||||
setSelectedSite(site);
|
||||
}}
|
||||
>
|
||||
<CheckIcon
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
site.siteId ===
|
||||
proxyTarget.siteId
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{site.name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
<SitesSelector
|
||||
orgId={orgId}
|
||||
selectedSite={selectedSite}
|
||||
onSelectSite={(site) => {
|
||||
updateTarget(proxyTarget.targetId, {
|
||||
siteId: site.siteId,
|
||||
siteType: site.type,
|
||||
siteName: site.name
|
||||
});
|
||||
setSelectedSite(site);
|
||||
}}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
|
||||
91
src/components/site-selector.tsx
Normal file
91
src/components/site-selector.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import { orgQueries } from "@app/lib/queries";
|
||||
import type { ListSitesResponse } from "@server/routers/site";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useMemo, useState } from "react";
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList
|
||||
} from "./ui/command";
|
||||
import { cn } from "@app/lib/cn";
|
||||
import { CheckIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useDebounce } from "use-debounce";
|
||||
|
||||
type Selectedsite = Pick<
|
||||
ListSitesResponse["sites"][number],
|
||||
"name" | "siteId" | "type"
|
||||
>;
|
||||
|
||||
export type SitesSelectorProps = {
|
||||
orgId: string;
|
||||
selectedSite?: Selectedsite | null;
|
||||
onSelectSite: (selected: Selectedsite) => void;
|
||||
};
|
||||
|
||||
export function SitesSelector({
|
||||
orgId,
|
||||
selectedSite,
|
||||
onSelectSite
|
||||
}: SitesSelectorProps) {
|
||||
const t = useTranslations();
|
||||
const [siteSearchQuery, setSiteSearchQuery] = useState("");
|
||||
const [debouncedQuery] = useDebounce(siteSearchQuery, 150);
|
||||
|
||||
const { data: sites = [] } = useQuery(
|
||||
orgQueries.sites({
|
||||
orgId,
|
||||
query: debouncedQuery,
|
||||
perPage: 10
|
||||
})
|
||||
);
|
||||
|
||||
// always include the selected site in the list of sites shown
|
||||
const sitesShown = useMemo(() => {
|
||||
const allSites: Array<Selectedsite> = [...sites];
|
||||
if (
|
||||
selectedSite &&
|
||||
!allSites.find((site) => site.siteId === selectedSite?.siteId)
|
||||
) {
|
||||
allSites.unshift(selectedSite);
|
||||
}
|
||||
return allSites;
|
||||
}, [sites, selectedSite]);
|
||||
|
||||
return (
|
||||
<Command shouldFilter={false}>
|
||||
<CommandInput
|
||||
placeholder={t("siteSearch")}
|
||||
value={siteSearchQuery}
|
||||
onValueChange={(v) => setSiteSearchQuery(v)}
|
||||
/>
|
||||
<CommandList>
|
||||
<CommandEmpty>{t("siteNotFound")}</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{sitesShown.map((site) => (
|
||||
<CommandItem
|
||||
key={site.siteId}
|
||||
value={`${site.siteId}:${site.name}`}
|
||||
onSelect={() => {
|
||||
onSelectSite(site);
|
||||
}}
|
||||
>
|
||||
<CheckIcon
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
site.siteId === selectedSite?.siteId
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{site.name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user