From 0cbcc0c29c0f38b1f0a01c56e0d7ae3569e3f5d3 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Sun, 12 Apr 2026 14:58:55 -0700 Subject: [PATCH] remove extra sites query --- .../siteResource/listAllSiteResourcesByOrg.ts | 4 +- .../settings/resources/client/page.tsx | 3 +- src/components/ClientResourcesTable.tsx | 168 +++++++++--------- .../CreateInternalResourceDialog.tsx | 3 - src/components/EditInternalResourceDialog.tsx | 3 - src/components/InternalResourceForm.tsx | 49 +---- 6 files changed, 97 insertions(+), 133 deletions(-) diff --git a/server/routers/siteResource/listAllSiteResourcesByOrg.ts b/server/routers/siteResource/listAllSiteResourcesByOrg.ts index 3495d9767..de9083c2c 100644 --- a/server/routers/siteResource/listAllSiteResourcesByOrg.ts +++ b/server/routers/siteResource/listAllSiteResourcesByOrg.ts @@ -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)); diff --git a/src/app/[orgId]/settings/resources/client/page.tsx b/src/app/[orgId]/settings/resources/client/page.tsx index 4d3b48c6c..f63563cc9 100644 --- a/src/app/[orgId]/settings/resources/client/page.tsx +++ b/src/app/[orgId]/settings/resources/client/page.tsx @@ -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, diff --git a/src/components/ClientResourcesTable.tsx b/src/components/ClientResourcesTable.tsx index 4822f358e..fc1a6a6f3 100644 --- a/src/components/ClientResourcesTable.tsx +++ b/src/components/ClientResourcesTable.tsx @@ -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 -; +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 ( -
- {visible.map((site) => ( - - - - ))} - {overflow.length > 0 ? ( - - ) : null} -
- ); + 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 -; + } + + const aggregate = aggregateSitesStatus(resourceSites); + const countLabel = t("multiSitesSelectorSitesCount", { + count: resourceSites.length + }); return ( - - + + - - setOpen(true)} - onMouseLeave={() => setOpen(false)} - > -
    - {sites.map((site) => ( -
  • + + + {resourceSites.map((site) => { + const isOnline = site.online; + return ( + - + + + {isOnline ? t("online") : t("offline")} + -
  • - ))} -
-
-
+ + ); + })} + + ); } @@ -243,8 +247,6 @@ export default function ClientResourcesTable({ useState(); 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 ( - ); } @@ -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(() => { diff --git a/src/components/CreateInternalResourceDialog.tsx b/src/components/CreateInternalResourceDialog.tsx index 1ad7b3632..c0483e35d 100644 --- a/src/components/CreateInternalResourceDialog.tsx +++ b/src/components/CreateInternalResourceDialog.tsx @@ -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({ 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" diff --git a/src/components/InternalResourceForm.tsx b/src/components/InternalResourceForm.tsx index 6bc807046..0d98fb30b 100644 --- a/src/components/InternalResourceForm.tsx +++ b/src/components/InternalResourceForm.tsx @@ -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; - 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(() => 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({ @@ -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(() => {