diff --git a/server/routers/siteResource/listAllSiteResourcesByOrg.ts b/server/routers/siteResource/listAllSiteResourcesByOrg.ts
index ac243328c..2298e9cba 100644
--- a/server/routers/siteResource/listAllSiteResourcesByOrg.ts
+++ b/server/routers/siteResource/listAllSiteResourcesByOrg.ts
@@ -85,7 +85,27 @@ const listAllSiteResourcesByOrgQuerySchema = z.object({
type: "integer",
description:
"When set, only site resources associated with this site (via network) are returned"
- })
+ }),
+ labels: z
+ .preprocess((val) => {
+ if (val === undefined || val === null || val === "") {
+ return undefined;
+ }
+ if (Array.isArray(val)) {
+ return val;
+ }
+ // the array is returned as this
+ if (typeof val === "string") {
+ return val.split(",");
+ }
+ return undefined;
+ }, z.array(z.string()))
+ .optional()
+ .catch([])
+ .openapi({
+ type: "array",
+ description: "Filter by resource labels"
+ })
});
export type ListAllSiteResourcesByOrgResponse = PaginatedResponse<{
@@ -239,8 +259,16 @@ export async function listAllSiteResourcesByOrg(
}
const { orgId } = parsedParams.data;
- const { page, pageSize, query, mode, sort_by, order, siteId } =
- parsedQuery.data;
+ const {
+ page,
+ pageSize,
+ query,
+ mode,
+ sort_by,
+ order,
+ siteId,
+ labels: labelFilter
+ } = parsedQuery.data;
const isLabelFeatureEnabled = await isLicensedOrSubscribed(
orgId,
@@ -276,6 +304,22 @@ export async function listAllSiteResourcesByOrg(
conditions.push(eq(siteResources.mode, mode));
}
+ if (isLabelFeatureEnabled && labelFilter && labelFilter.length > 0) {
+ conditions.push(
+ inArray(
+ siteResources.siteResourceId,
+ db
+ .select({ id: siteResourceLabels.siteResourceId })
+ .from(siteResourceLabels)
+ .innerJoin(
+ labels,
+ eq(labels.labelId, siteResourceLabels.labelId)
+ )
+ .where(inArray(labels.name, labelFilter))
+ )
+ );
+ }
+
if (query) {
const q = "%" + query.toLowerCase() + "%";
const queryList = [
diff --git a/src/components/ClientResourcesTable.tsx b/src/components/ClientResourcesTable.tsx
index 156cc7f41..a14ad8346 100644
--- a/src/components/ClientResourcesTable.tsx
+++ b/src/components/ClientResourcesTable.tsx
@@ -64,6 +64,7 @@ import { usePaidStatus } from "@app/hooks/usePaidStatus";
import { tierMatrix } from "@server/lib/billing/tierMatrix";
import { LabelBadge } from "./label-badge";
import { LabelsSelector, type SelectedLabel } from "./labels-selector";
+import { LabelColumnFilterButton } from "./LabelColumnFilterButton";
export type InternalResourceSiteRow = ResourceSiteRow;
@@ -220,59 +221,6 @@ export default function ClientResourcesTable({
}
};
- function SiteCell({ resourceRow }: { resourceRow: InternalResourceRow }) {
- const { siteNames, siteNiceIds, orgId } = resourceRow;
-
- if (!siteNames || siteNames.length === 0) {
- return (
-
- {t("noSites", { defaultValue: "No sites" })}
-
- );
- }
-
- if (siteNames.length === 1) {
- return (
-
-
-
- );
- }
-
- return (
-
-
-
-
-
- {siteNames.map((siteName, idx) => (
-
-
- {siteName}
-
-
-
- ))}
-
-
- );
- }
-
const internalColumns = useMemo<
ExtendedColumnDef[]
>(() => {
@@ -585,9 +533,15 @@ export default function ClientResourcesTable({
id: "labels",
accessorKey: "labels",
header: () => (
-
- {t("labels")}
-
+
+ handleFilterChange("labels", value)
+ }
+ label={t("labels")}
+ className="p-3"
+ />
),
cell: ({ row }: { row: { original: InternalResourceRow } }) => (
searchParams.append(column, val));
}
filter({
searchParams