remove extra sites query

This commit is contained in:
miloschwartz
2026-04-12 14:58:55 -07:00
parent b5e239d1ad
commit 0cbcc0c29c
6 changed files with 97 additions and 133 deletions

View File

@@ -76,6 +76,7 @@ export type ListAllSiteResourcesByOrgResponse = PaginatedResponse<{
siteName: string;
siteNiceId: string;
siteAddress: string | null;
siteOnline: boolean;
})[];
}>;
@@ -106,7 +107,8 @@ function querySiteResourcesBase() {
fullDomain: siteResources.fullDomain,
siteName: sites.name,
siteNiceId: sites.niceId,
siteAddress: sites.address
siteAddress: sites.address,
siteOnline: sites.online
})
.from(siteResources)
.innerJoin(sites, eq(siteResources.siteId, sites.siteId));

View File

@@ -73,7 +73,8 @@ export default async function ClientResourcesPage(
{
siteId: siteResource.siteId,
siteName: siteResource.siteName,
siteNiceId: siteResource.siteNiceId
siteNiceId: siteResource.siteNiceId,
online: siteResource.siteOnline
}
],
siteName: siteResource.siteName,

View File

@@ -20,7 +20,7 @@ import {
ArrowDown01Icon,
ArrowUp10Icon,
ArrowUpDown,
ArrowUpRight,
ChevronDown,
ChevronsUpDownIcon,
MoreHorizontal
} from "lucide-react";
@@ -38,16 +38,13 @@ import { ControlledDataTable } from "./ui/controlled-data-table";
import { useNavigationContext } from "@app/hooks/useNavigationContext";
import { useDebouncedCallback } from "use-debounce";
import { ColumnFilterButton } from "./ColumnFilterButton";
import {
Popover,
PopoverContent,
PopoverTrigger
} from "@app/components/ui/popover";
import { cn } from "@app/lib/cn";
export type InternalResourceSiteRow = {
siteId: number;
siteName: string;
siteNiceId: string;
online: boolean;
};
export type InternalResourceRow = {
@@ -113,99 +110,106 @@ function isSafeUrlForLink(href: string): boolean {
}
}
const MAX_SITE_LINKS = 3;
type AggregateSitesStatus = "allOnline" | "partial" | "allOffline";
function ClientResourceSiteLinks({
orgId,
sites
}: {
orgId: string;
sites: InternalResourceSiteRow[];
}) {
if (sites.length === 0) {
return <span>-</span>;
function aggregateSitesStatus(
resourceSites: InternalResourceSiteRow[]
): AggregateSitesStatus {
if (resourceSites.length === 0) {
return "allOffline";
}
const visible = sites.slice(0, MAX_SITE_LINKS);
const overflow = sites.slice(MAX_SITE_LINKS);
return (
<div className="flex flex-wrap items-center gap-1">
{visible.map((site) => (
<Link
key={site.siteId}
href={`/${orgId}/settings/sites/${site.siteNiceId}`}
>
<Button
variant="outline"
size="sm"
className="w-full gap-1"
>
<span className="max-w-[10rem] truncate">
{site.siteName}
</span>
<ArrowUpRight className="h-3 w-3 shrink-0" />
</Button>
</Link>
))}
{overflow.length > 0 ? (
<OverflowSitesPopover orgId={orgId} sites={overflow} />
) : null}
</div>
);
const onlineCount = resourceSites.filter((rs) => rs.online).length;
if (onlineCount === resourceSites.length) return "allOnline";
if (onlineCount > 0) return "partial";
return "allOffline";
}
function OverflowSitesPopover({
function aggregateStatusDotClass(status: AggregateSitesStatus): string {
switch (status) {
case "allOnline":
return "bg-green-500";
case "partial":
return "bg-yellow-500";
case "allOffline":
default:
return "bg-gray-500";
}
}
function ClientResourceSitesStatusCell({
orgId,
sites
resourceSites
}: {
orgId: string;
sites: InternalResourceSiteRow[];
resourceSites: InternalResourceSiteRow[];
}) {
const [open, setOpen] = useState(false);
const t = useTranslations();
if (resourceSites.length === 0) {
return <span>-</span>;
}
const aggregate = aggregateSitesStatus(resourceSites);
const countLabel = t("multiSitesSelectorSitesCount", {
count: resourceSites.length
});
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
type="button"
variant="ghost"
size="sm"
variant="outline"
className="gap-1 px-2 font-normal"
onMouseEnter={() => setOpen(true)}
onMouseLeave={() => setOpen(false)}
className="flex h-8 items-center gap-2 px-0 font-normal"
>
+{sites.length}
<div
className={cn(
"h-2 w-2 shrink-0 rounded-full",
aggregateStatusDotClass(aggregate)
)}
/>
<span className="text-sm tabular-nums">{countLabel}</span>
<ChevronDown className="h-3 w-3 shrink-0" />
</Button>
</PopoverTrigger>
<PopoverContent
align="start"
side="top"
className="w-auto max-w-xs p-2"
onMouseEnter={() => setOpen(true)}
onMouseLeave={() => setOpen(false)}
>
<ul className="flex flex-col gap-1.5 text-sm">
{sites.map((site) => (
<li key={site.siteId}>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="min-w-56">
{resourceSites.map((site) => {
const isOnline = site.online;
return (
<DropdownMenuItem key={site.siteId} asChild>
<Link
href={`/${orgId}/settings/sites/${site.siteNiceId}`}
className="flex cursor-pointer items-center justify-between gap-4"
>
<Button
variant="outline"
size="sm"
className="w-full justify-start gap-1"
>
<div className="flex min-w-0 items-center gap-2">
<div
className={cn(
"h-2 w-2 shrink-0 rounded-full",
isOnline
? "bg-green-500"
: "bg-gray-500"
)}
/>
<span className="truncate">
{site.siteName}
</span>
<ArrowUpRight className="ml-auto h-3 w-3 shrink-0" />
</Button>
</div>
<span
className={cn(
"shrink-0 capitalize",
isOnline
? "text-green-600"
: "text-muted-foreground"
)}
>
{isOnline ? t("online") : t("offline")}
</span>
</Link>
</li>
))}
</ul>
</PopoverContent>
</Popover>
</DropdownMenuItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
);
}
@@ -243,8 +247,6 @@ export default function ClientResourcesTable({
useState<InternalResourceRow | null>();
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
const { data: sites = [] } = useQuery(orgQueries.sites({ orgId }));
const [isRefreshing, startTransition] = useTransition();
const refreshData = () => {
@@ -339,9 +341,9 @@ export default function ClientResourcesTable({
cell: ({ row }) => {
const resourceRow = row.original;
return (
<ClientResourceSiteLinks
<ClientResourceSitesStatusCell
orgId={resourceRow.orgId}
sites={resourceRow.sites}
resourceSites={resourceRow.sites}
/>
);
}
@@ -599,7 +601,6 @@ export default function ClientResourcesTable({
setOpen={setIsEditDialogOpen}
resource={editingResource}
orgId={orgId}
sites={sites}
onSuccess={() => {
// Delay refresh to allow modal to close smoothly
setTimeout(() => {
@@ -614,7 +615,6 @@ export default function ClientResourcesTable({
open={isCreateDialogOpen}
setOpen={setIsCreateDialogOpen}
orgId={orgId}
sites={sites}
onSuccess={() => {
// Delay refresh to allow modal to close smoothly
setTimeout(() => {

View File

@@ -31,7 +31,6 @@ type CreateInternalResourceDialogProps = {
open: boolean;
setOpen: (val: boolean) => void;
orgId: string;
sites: Site[];
onSuccess?: () => void;
};
@@ -39,7 +38,6 @@ export default function CreateInternalResourceDialog({
open,
setOpen,
orgId,
sites,
onSuccess
}: CreateInternalResourceDialogProps) {
const t = useTranslations();
@@ -155,7 +153,6 @@ export default function CreateInternalResourceDialog({
<InternalResourceForm
variant="create"
open={open}
sites={sites}
orgId={orgId}
formId="create-internal-resource-form"
onSubmit={handleSubmit}

View File

@@ -34,7 +34,6 @@ type EditInternalResourceDialogProps = {
setOpen: (val: boolean) => void;
resource: InternalResourceData;
orgId: string;
sites: Site[];
onSuccess?: () => void;
};
@@ -43,7 +42,6 @@ export default function EditInternalResourceDialog({
setOpen,
resource,
orgId,
sites,
onSuccess
}: EditInternalResourceDialogProps) {
const t = useTranslations();
@@ -174,7 +172,6 @@ export default function EditInternalResourceDialog({
variant="edit"
open={open}
resource={resource}
sites={sites}
orgId={orgId}
siteResourceId={resource.id}
formId="edit-internal-resource-form"

View File

@@ -159,18 +159,7 @@ const tagSchema = z.object({ id: z.string(), text: z.string() });
function buildSelectedSitesForResource(
resource: InternalResourceData,
catalog: Site[]
): Selectedsite[] {
const fromCatalog = catalog.find((s) => s.siteId === resource.siteId);
if (fromCatalog) {
return [
{
name: fromCatalog.name,
siteId: fromCatalog.siteId,
type: fromCatalog.type
}
];
}
return [
{
name: resource.siteName,
@@ -207,7 +196,6 @@ type InternalResourceFormProps = {
variant: "create" | "edit";
resource?: InternalResourceData;
open?: boolean;
sites: Site[];
orgId: string;
siteResourceId?: number;
formId: string;
@@ -218,7 +206,6 @@ export function InternalResourceForm({
variant,
resource,
open,
sites,
orgId,
siteResourceId,
formId,
@@ -375,8 +362,6 @@ export function InternalResourceForm({
type FormData = z.infer<typeof formSchema>;
const availableSites = sites.filter((s) => s.type === "newt");
const rolesQuery = useQuery(orgQueries.roles({ orgId }));
const usersQuery = useQuery(orgQueries.users({ orgId }));
const clientsQuery = useQuery(orgQueries.machineClients({ orgId }));
@@ -517,7 +502,7 @@ export function InternalResourceForm({
}
: {
name: "",
siteIds: availableSites[0] ? [availableSites[0].siteId] : [],
siteIds: [],
mode: "host",
destination: "",
alias: null,
@@ -539,16 +524,8 @@ export function InternalResourceForm({
const [selectedSites, setSelectedSites] = useState<Selectedsite[]>(() =>
variant === "edit" && resource
? buildSelectedSitesForResource(resource, sites)
: availableSites[0]
? [
{
name: availableSites[0].name,
siteId: availableSites[0].siteId,
type: availableSites[0].type
}
]
: []
? buildSelectedSitesForResource(resource)
: []
);
const form = useForm<FormData>({
@@ -580,7 +557,7 @@ export function InternalResourceForm({
if (variant === "create" && open) {
form.reset({
name: "",
siteIds: availableSites[0] ? [availableSites[0].siteId] : [],
siteIds: [],
mode: "host",
destination: "",
alias: null,
@@ -599,23 +576,13 @@ export function InternalResourceForm({
users: [],
clients: []
});
setSelectedSites(
availableSites[0]
? [
{
name: availableSites[0].name,
siteId: availableSites[0].siteId,
type: availableSites[0].type
}
]
: []
);
setSelectedSites([]);
setTcpPortMode("all");
setUdpPortMode("all");
setTcpCustomPorts("");
setUdpCustomPorts("");
}
}, [variant, open, form, sites]);
}, [variant, open, form]);
// Reset when edit dialog opens / resource changes
useEffect(() => {
@@ -644,7 +611,7 @@ export function InternalResourceForm({
clients: []
});
setSelectedSites(
buildSelectedSitesForResource(resource, sites)
buildSelectedSitesForResource(resource)
);
setTcpPortMode(
getPortModeFromString(resource.tcpPortRangeString)
@@ -667,7 +634,7 @@ export function InternalResourceForm({
previousResourceId.current = resource.id;
}
}
}, [variant, resource, form, sites]);
}, [variant, resource, form]);
// When edit dialog closes, clear previousResourceId so next open (for any resource) resets from fresh data
useEffect(() => {