label filter column for private resources

This commit is contained in:
Fred KISSIE
2026-05-26 23:36:07 +02:00
parent 91883397e6
commit 36fbd8818c
2 changed files with 61 additions and 61 deletions

View File

@@ -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 = [

View File

@@ -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 (
<span className="text-muted-foreground">
{t("noSites", { defaultValue: "No sites" })}
</span>
);
}
if (siteNames.length === 1) {
return (
<Link href={`/${orgId}/settings/sites/${siteNiceIds[0]}`}>
<Button variant="outline">
{siteNames[0]}
<ArrowUpRight className="ml-2 h-4 w-4" />
</Button>
</Link>
);
}
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="sm"
className="flex items-center gap-2"
>
<span>
{siteNames.length} {t("sites")}
</span>
<ChevronDown className="h-3 w-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
{siteNames.map((siteName, idx) => (
<DropdownMenuItem key={siteNiceIds[idx]} asChild>
<Link
href={`/${orgId}/settings/sites/${siteNiceIds[idx]}`}
className="flex items-center gap-2 cursor-pointer"
>
{siteName}
<ArrowUpRight className="h-3 w-3" />
</Link>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
);
}
const internalColumns = useMemo<
ExtendedColumnDef<InternalResourceRow>[]
>(() => {
@@ -585,9 +533,15 @@ export default function ClientResourcesTable({
id: "labels",
accessorKey: "labels",
header: () => (
<span className="p-3 text-end w-full inline-block">
{t("labels")}
</span>
<LabelColumnFilterButton
orgId={orgId}
selectedValues={searchParams.getAll("labels")}
onSelectedValuesChange={(value) =>
handleFilterChange("labels", value)
}
label={t("labels")}
className="p-3"
/>
),
cell: ({ row }: { row: { original: InternalResourceRow } }) => (
<ClientResourceLabelCell
@@ -603,13 +557,15 @@ export default function ClientResourcesTable({
function handleFilterChange(
column: string,
value: string | undefined | null
value: string | undefined | null | string[]
) {
searchParams.delete(column);
searchParams.delete("page");
if (value) {
if (typeof value === "string") {
searchParams.set(column, value);
} else if (value) {
value.forEach((val) => searchParams.append(column, val));
}
filter({
searchParams