mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-15 17:36:37 +00:00
🚧 POC: pagination in sites table
This commit is contained in:
@@ -74,18 +74,20 @@ const listSitesParamsSchema = z.strictObject({
|
||||
});
|
||||
|
||||
const listSitesSchema = z.object({
|
||||
limit: z
|
||||
.string()
|
||||
pageSize: z.coerce
|
||||
.number<string>() // for prettier formatting
|
||||
.int()
|
||||
.positive()
|
||||
.optional()
|
||||
.default("1")
|
||||
.transform(Number)
|
||||
.pipe(z.int().positive()),
|
||||
offset: z
|
||||
.string()
|
||||
.catch(20)
|
||||
.default(20),
|
||||
page: z.coerce
|
||||
.number<string>() // for prettier formatting
|
||||
.int()
|
||||
.min(0)
|
||||
.optional()
|
||||
.default("0")
|
||||
.transform(Number)
|
||||
.pipe(z.int().nonnegative())
|
||||
.catch(1)
|
||||
.default(1)
|
||||
});
|
||||
|
||||
function querySites(orgId: string, accessibleSiteIds: number[]) {
|
||||
@@ -130,7 +132,7 @@ type SiteWithUpdateAvailable = Awaited<ReturnType<typeof querySites>>[0] & {
|
||||
|
||||
export type ListSitesResponse = {
|
||||
sites: SiteWithUpdateAvailable[];
|
||||
pagination: { total: number; limit: number; offset: number; };
|
||||
pagination: { total: number; pageSize: number; page: number };
|
||||
};
|
||||
|
||||
registry.registerPath({
|
||||
@@ -160,7 +162,7 @@ export async function listSites(
|
||||
)
|
||||
);
|
||||
}
|
||||
const { limit, offset } = parsedQuery.data;
|
||||
const { pageSize, page } = parsedQuery.data;
|
||||
|
||||
const parsedParams = listSitesParamsSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
@@ -216,7 +218,9 @@ export async function listSites(
|
||||
)
|
||||
);
|
||||
|
||||
const sitesList = await baseQuery.limit(limit).offset(offset);
|
||||
const sitesList = await baseQuery
|
||||
.limit(pageSize)
|
||||
.offset(pageSize * (page - 1));
|
||||
const totalCountResult = await countQuery;
|
||||
const totalCount = totalCountResult[0].count;
|
||||
|
||||
@@ -267,8 +271,8 @@ export async function listSites(
|
||||
sites: sitesWithUpdates,
|
||||
pagination: {
|
||||
total: totalCount,
|
||||
limit,
|
||||
offset
|
||||
pageSize,
|
||||
page
|
||||
}
|
||||
},
|
||||
success: true,
|
||||
|
||||
@@ -9,19 +9,30 @@ import { getTranslations } from "next-intl/server";
|
||||
|
||||
type SitesPageProps = {
|
||||
params: Promise<{ orgId: string }>;
|
||||
searchParams: Promise<Record<string, string>>;
|
||||
};
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default async function SitesPage(props: SitesPageProps) {
|
||||
const params = await props.params;
|
||||
|
||||
const searchParams = new URLSearchParams(await props.searchParams);
|
||||
|
||||
let sites: ListSitesResponse["sites"] = [];
|
||||
let pagination: ListSitesResponse["pagination"] = {
|
||||
total: 0,
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
};
|
||||
try {
|
||||
const res = await internal.get<AxiosResponse<ListSitesResponse>>(
|
||||
`/org/${params.orgId}/sites`,
|
||||
`/org/${params.orgId}/sites?${searchParams.toString()}`,
|
||||
await authCookieHeader()
|
||||
);
|
||||
sites = res.data.data.sites;
|
||||
const responseData = res.data.data;
|
||||
sites = responseData.sites;
|
||||
pagination = responseData.pagination;
|
||||
} catch (e) {}
|
||||
|
||||
const t = await getTranslations();
|
||||
@@ -67,7 +78,17 @@ export default async function SitesPage(props: SitesPageProps) {
|
||||
|
||||
<SitesBanner />
|
||||
|
||||
<SitesTable sites={siteRows} orgId={params.orgId} />
|
||||
<SitesTable
|
||||
sites={siteRows}
|
||||
orgId={params.orgId}
|
||||
pagination={{
|
||||
pageCount: Math.ceil(
|
||||
pagination.total / pagination.pageSize
|
||||
),
|
||||
pageIndex: pagination.page - 1,
|
||||
pageSize: pagination.pageSize
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { DataTable } from "@app/components/ui/data-table";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
interface DataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[];
|
||||
data: TData[];
|
||||
createSite?: () => void;
|
||||
onRefresh?: () => void;
|
||||
isRefreshing?: boolean;
|
||||
columnVisibility?: Record<string, boolean>;
|
||||
enableColumnVisibility?: boolean;
|
||||
}
|
||||
|
||||
export function SitesDataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
createSite,
|
||||
onRefresh,
|
||||
isRefreshing,
|
||||
columnVisibility,
|
||||
enableColumnVisibility
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const t = useTranslations();
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={data}
|
||||
persistPageSize="sites-table"
|
||||
title={t("sites")}
|
||||
searchPlaceholder={t("searchSitesProgress")}
|
||||
searchColumn="name"
|
||||
onAdd={createSite}
|
||||
addButtonText={t("siteAdd")}
|
||||
onRefresh={onRefresh}
|
||||
isRefreshing={isRefreshing}
|
||||
defaultSort={{
|
||||
id: "name",
|
||||
desc: false
|
||||
}}
|
||||
columnVisibility={columnVisibility}
|
||||
enableColumnVisibility={enableColumnVisibility}
|
||||
stickyLeftColumn="name"
|
||||
stickyRightColumn="actions"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
|
||||
import { SitesDataTable } from "@app/components/SitesDataTable";
|
||||
|
||||
import { Badge } from "@app/components/ui/badge";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { ExtendedColumnDef } from "@app/components/ui/data-table";
|
||||
import {
|
||||
DataTable,
|
||||
ExtendedColumnDef,
|
||||
type DataTablePaginationState
|
||||
} from "@app/components/ui/data-table";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -26,7 +30,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||
import { useEffect, useState, useTransition } from "react";
|
||||
|
||||
export type SiteRow = {
|
||||
@@ -48,15 +52,21 @@ export type SiteRow = {
|
||||
|
||||
type SitesTableProps = {
|
||||
sites: SiteRow[];
|
||||
pagination: DataTablePaginationState;
|
||||
orgId: string;
|
||||
};
|
||||
|
||||
export default function SitesTable({ sites, orgId }: SitesTableProps) {
|
||||
export default function SitesTable({
|
||||
sites,
|
||||
orgId,
|
||||
pagination
|
||||
}: SitesTableProps) {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const pathname = usePathname();
|
||||
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const [selectedSite, setSelectedSite] = useState<SiteRow | null>(null);
|
||||
const [rows, setRows] = useState<SiteRow[]>(sites);
|
||||
const [isRefreshing, startTransition] = useTransition();
|
||||
|
||||
const api = createApiClient(useEnvContext());
|
||||
@@ -87,10 +97,6 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
|
||||
.then(() => {
|
||||
router.refresh();
|
||||
setIsDeleteModalOpen(false);
|
||||
|
||||
const newRows = rows.filter((row) => row.id !== siteId);
|
||||
|
||||
setRows(newRows);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -413,6 +419,11 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
|
||||
}
|
||||
];
|
||||
|
||||
console.log({
|
||||
sites,
|
||||
pagination
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{selectedSite && (
|
||||
@@ -429,27 +440,50 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
|
||||
</div>
|
||||
}
|
||||
buttonText={t("siteConfirmDelete")}
|
||||
onConfirm={async () => deleteSite(selectedSite!.id)}
|
||||
onConfirm={async () =>
|
||||
startTransition(() => deleteSite(selectedSite!.id))
|
||||
}
|
||||
string={selectedSite.name}
|
||||
title={t("siteDelete")}
|
||||
/>
|
||||
)}
|
||||
|
||||
<SitesDataTable
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={rows}
|
||||
createSite={() =>
|
||||
router.push(`/${orgId}/settings/sites/create`)
|
||||
}
|
||||
data={sites}
|
||||
persistPageSize="sites-table"
|
||||
title={t("sites")}
|
||||
searchPlaceholder={t("searchSitesProgress")}
|
||||
manualFiltering
|
||||
pagination={pagination}
|
||||
onPaginationChange={(newPage) => {
|
||||
console.log({
|
||||
newPage
|
||||
});
|
||||
const sp = new URLSearchParams(searchParams);
|
||||
sp.set("page", (newPage.pageIndex + 1).toString());
|
||||
sp.set("pageSize", newPage.pageSize.toString());
|
||||
startTransition(() =>
|
||||
router.push(`${pathname}?${sp.toString()}`)
|
||||
);
|
||||
}}
|
||||
onAdd={() => router.push(`/${orgId}/settings/sites/create`)}
|
||||
addButtonText={t("siteAdd")}
|
||||
onRefresh={() => startTransition(refreshData)}
|
||||
isRefreshing={isRefreshing}
|
||||
defaultSort={{
|
||||
id: "name",
|
||||
desc: false
|
||||
}}
|
||||
columnVisibility={{
|
||||
niceId: false,
|
||||
nice: false,
|
||||
exitNode: false,
|
||||
address: false
|
||||
}}
|
||||
enableColumnVisibility={true}
|
||||
enableColumnVisibility
|
||||
stickyLeftColumn="name"
|
||||
stickyRightColumn="actions"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -151,11 +151,20 @@ type DataTableFilter = {
|
||||
label: string;
|
||||
options: FilterOption[];
|
||||
multiSelect?: boolean;
|
||||
filterFn: (row: any, selectedValues: (string | number | boolean)[]) => boolean;
|
||||
filterFn: (
|
||||
row: any,
|
||||
selectedValues: (string | number | boolean)[]
|
||||
) => boolean;
|
||||
defaultValues?: (string | number | boolean)[];
|
||||
displayMode?: "label" | "calculated"; // How to display the filter button text
|
||||
};
|
||||
|
||||
export type DataTablePaginationState = PaginationState & {
|
||||
pageCount: number;
|
||||
};
|
||||
|
||||
export type DataTablePaginationUpdateFn = (newPage: PaginationState) => void;
|
||||
|
||||
type DataTableProps<TData, TValue> = {
|
||||
columns: ExtendedColumnDef<TData, TValue>[];
|
||||
data: TData[];
|
||||
@@ -178,6 +187,11 @@ type DataTableProps<TData, TValue> = {
|
||||
defaultPageSize?: number;
|
||||
columnVisibility?: Record<string, boolean>;
|
||||
enableColumnVisibility?: boolean;
|
||||
manualFiltering?: boolean;
|
||||
onSearch?: (input: string) => void;
|
||||
searchValue?: string;
|
||||
pagination?: DataTablePaginationState;
|
||||
onPaginationChange?: DataTablePaginationUpdateFn;
|
||||
persistColumnVisibility?: boolean | string;
|
||||
stickyLeftColumn?: string; // Column ID or accessorKey for left sticky column
|
||||
stickyRightColumn?: string; // Column ID or accessorKey for right sticky column (typically "actions")
|
||||
@@ -203,7 +217,12 @@ export function DataTable<TData, TValue>({
|
||||
columnVisibility: defaultColumnVisibility,
|
||||
enableColumnVisibility = false,
|
||||
persistColumnVisibility = false,
|
||||
manualFiltering = false,
|
||||
pagination: paginationState,
|
||||
stickyLeftColumn,
|
||||
onSearch,
|
||||
searchValue,
|
||||
onPaginationChange,
|
||||
stickyRightColumn
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const t = useTranslations();
|
||||
@@ -248,22 +267,25 @@ export function DataTable<TData, TValue>({
|
||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>(
|
||||
initialColumnVisibility
|
||||
);
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
const [_pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: pageSize
|
||||
});
|
||||
|
||||
const pagination = paginationState ?? _pagination;
|
||||
|
||||
const [activeTab, setActiveTab] = useState<string>(
|
||||
defaultTab || tabs?.[0]?.id || ""
|
||||
);
|
||||
const [activeFilters, setActiveFilters] = useState<Record<string, (string | number | boolean)[]>>(
|
||||
() => {
|
||||
const initial: Record<string, (string | number | boolean)[]> = {};
|
||||
filters?.forEach((filter) => {
|
||||
initial[filter.id] = filter.defaultValues || [];
|
||||
});
|
||||
return initial;
|
||||
}
|
||||
);
|
||||
const [activeFilters, setActiveFilters] = useState<
|
||||
Record<string, (string | number | boolean)[]>
|
||||
>(() => {
|
||||
const initial: Record<string, (string | number | boolean)[]> = {};
|
||||
filters?.forEach((filter) => {
|
||||
initial[filter.id] = filter.defaultValues || [];
|
||||
});
|
||||
return initial;
|
||||
});
|
||||
|
||||
// Track initial values to avoid storing defaults on first render
|
||||
const initialPageSize = useRef(pageSize);
|
||||
@@ -309,7 +331,16 @@ export function DataTable<TData, TValue>({
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onGlobalFilterChange: setGlobalFilter,
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onPaginationChange: setPagination,
|
||||
onPaginationChange: onPaginationChange
|
||||
? (state) => {
|
||||
const newState =
|
||||
typeof state === "function" ? state(pagination) : state;
|
||||
onPaginationChange(newState);
|
||||
}
|
||||
: setPagination,
|
||||
manualFiltering,
|
||||
manualPagination: !!paginationState,
|
||||
pageCount: paginationState?.pageCount,
|
||||
initialState: {
|
||||
pagination: {
|
||||
pageSize: pageSize,
|
||||
@@ -368,11 +399,11 @@ export function DataTable<TData, TValue>({
|
||||
setActiveFilters((prev) => {
|
||||
const currentValues = prev[filterId] || [];
|
||||
const filter = filters?.find((f) => f.id === filterId);
|
||||
|
||||
|
||||
if (!filter) return prev;
|
||||
|
||||
let newValues: (string | number | boolean)[];
|
||||
|
||||
|
||||
if (filter.multiSelect) {
|
||||
// Multi-select: add or remove the value
|
||||
if (checked) {
|
||||
@@ -397,7 +428,7 @@ export function DataTable<TData, TValue>({
|
||||
// Calculate display text for a filter based on selected values
|
||||
const getFilterDisplayText = (filter: DataTableFilter): string => {
|
||||
const selectedValues = activeFilters[filter.id] || [];
|
||||
|
||||
|
||||
if (selectedValues.length === 0) {
|
||||
return filter.label;
|
||||
}
|
||||
@@ -477,12 +508,14 @@ export function DataTable<TData, TValue>({
|
||||
<div className="relative w-full sm:max-w-sm">
|
||||
<Input
|
||||
placeholder={searchPlaceholder}
|
||||
value={globalFilter ?? ""}
|
||||
onChange={(e) =>
|
||||
table.setGlobalFilter(
|
||||
String(e.target.value)
|
||||
)
|
||||
}
|
||||
value={searchValue ?? globalFilter ?? ""}
|
||||
onChange={(e) => {
|
||||
onSearch
|
||||
? onSearch(e.currentTarget.value)
|
||||
: table.setGlobalFilter(
|
||||
String(e.target.value)
|
||||
);
|
||||
}}
|
||||
className="w-full pl-8"
|
||||
/>
|
||||
<Search className="h-4 w-4 absolute left-2 top-1/2 transform -translate-y-1/2 text-muted-foreground" />
|
||||
@@ -490,13 +523,17 @@ export function DataTable<TData, TValue>({
|
||||
{filters && filters.length > 0 && (
|
||||
<div className="flex gap-2">
|
||||
{filters.map((filter) => {
|
||||
const selectedValues = activeFilters[filter.id] || [];
|
||||
const hasActiveFilters = selectedValues.length > 0;
|
||||
const displayMode = filter.displayMode || filterDisplayMode;
|
||||
const displayText = displayMode === "calculated"
|
||||
? getFilterDisplayText(filter)
|
||||
: filter.label;
|
||||
|
||||
const selectedValues =
|
||||
activeFilters[filter.id] || [];
|
||||
const hasActiveFilters =
|
||||
selectedValues.length > 0;
|
||||
const displayMode =
|
||||
filter.displayMode || filterDisplayMode;
|
||||
const displayText =
|
||||
displayMode === "calculated"
|
||||
? getFilterDisplayText(filter)
|
||||
: filter.label;
|
||||
|
||||
return (
|
||||
<DropdownMenu key={filter.id}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
@@ -507,37 +544,54 @@ export function DataTable<TData, TValue>({
|
||||
>
|
||||
<Filter className="h-4 w-4 mr-2" />
|
||||
{displayText}
|
||||
{displayMode === "label" && hasActiveFilters && (
|
||||
<span className="ml-2 bg-muted text-foreground rounded-full px-2 py-0.5 text-xs">
|
||||
{selectedValues.length}
|
||||
</span>
|
||||
)}
|
||||
{displayMode === "label" &&
|
||||
hasActiveFilters && (
|
||||
<span className="ml-2 bg-muted text-foreground rounded-full px-2 py-0.5 text-xs">
|
||||
{
|
||||
selectedValues.length
|
||||
}
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="w-48">
|
||||
<DropdownMenuContent
|
||||
align="start"
|
||||
className="w-48"
|
||||
>
|
||||
<DropdownMenuLabel>
|
||||
{filter.label}
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
{filter.options.map((option) => {
|
||||
const isChecked = selectedValues.includes(option.value);
|
||||
return (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={option.id}
|
||||
checked={isChecked}
|
||||
onCheckedChange={(checked) =>
|
||||
handleFilterChange(
|
||||
filter.id,
|
||||
option.value,
|
||||
{filter.options.map(
|
||||
(option) => {
|
||||
const isChecked =
|
||||
selectedValues.includes(
|
||||
option.value
|
||||
);
|
||||
return (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={option.id}
|
||||
checked={
|
||||
isChecked
|
||||
}
|
||||
onCheckedChange={(
|
||||
checked
|
||||
)
|
||||
}
|
||||
onSelect={(e) => e.preventDefault()}
|
||||
>
|
||||
{option.label}
|
||||
</DropdownMenuCheckboxItem>
|
||||
);
|
||||
})}
|
||||
) =>
|
||||
handleFilterChange(
|
||||
filter.id,
|
||||
option.value,
|
||||
checked
|
||||
)
|
||||
}
|
||||
onSelect={(e) =>
|
||||
e.preventDefault()
|
||||
}
|
||||
>
|
||||
{option.label}
|
||||
</DropdownMenuCheckboxItem>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
||||
@@ -113,7 +113,7 @@ export const orgQueries = {
|
||||
return res.data.data.clients;
|
||||
}
|
||||
}),
|
||||
users: ({ orgId }: { orgId: string; }) =>
|
||||
users: ({ orgId }: { orgId: string }) =>
|
||||
queryOptions({
|
||||
queryKey: ["ORG", orgId, "USERS"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
@@ -124,7 +124,7 @@ export const orgQueries = {
|
||||
return res.data.data.users;
|
||||
}
|
||||
}),
|
||||
roles: ({ orgId }: { orgId: string; }) =>
|
||||
roles: ({ orgId }: { orgId: string }) =>
|
||||
queryOptions({
|
||||
queryKey: ["ORG", orgId, "ROLES"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
@@ -136,7 +136,7 @@ export const orgQueries = {
|
||||
}
|
||||
}),
|
||||
|
||||
sites: ({ orgId }: { orgId: string; }) =>
|
||||
sites: ({ orgId }: { orgId: string }) =>
|
||||
queryOptions({
|
||||
queryKey: ["ORG", orgId, "SITES"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
@@ -147,7 +147,7 @@ export const orgQueries = {
|
||||
}
|
||||
}),
|
||||
|
||||
domains: ({ orgId }: { orgId: string; }) =>
|
||||
domains: ({ orgId }: { orgId: string }) =>
|
||||
queryOptions({
|
||||
queryKey: ["ORG", orgId, "DOMAINS"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
@@ -169,7 +169,7 @@ export const orgQueries = {
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
const res = await meta!.api.get<
|
||||
AxiosResponse<{
|
||||
idps: { idpId: number; name: string; }[];
|
||||
idps: { idpId: number; name: string }[];
|
||||
}>
|
||||
>(
|
||||
build === "saas" || useOrgOnlyIdp
|
||||
@@ -188,19 +188,20 @@ export const logAnalyticsFiltersSchema = z.object({
|
||||
.refine((val) => !isNaN(Date.parse(val)), {
|
||||
error: "timeStart must be a valid ISO date string"
|
||||
})
|
||||
.optional().catch(undefined),
|
||||
.optional()
|
||||
.catch(undefined),
|
||||
timeEnd: z
|
||||
.string()
|
||||
.refine((val) => !isNaN(Date.parse(val)), {
|
||||
error: "timeEnd must be a valid ISO date string"
|
||||
})
|
||||
.optional().catch(undefined),
|
||||
.optional()
|
||||
.catch(undefined),
|
||||
resourceId: z.coerce.number().optional().catch(undefined)
|
||||
});
|
||||
|
||||
export type LogAnalyticsFilters = z.TypeOf<typeof logAnalyticsFiltersSchema>;
|
||||
|
||||
|
||||
export const logQueries = {
|
||||
requestAnalytics: ({
|
||||
orgId,
|
||||
@@ -230,7 +231,7 @@ export const logQueries = {
|
||||
};
|
||||
|
||||
export const resourceQueries = {
|
||||
resourceUsers: ({ resourceId }: { resourceId: number; }) =>
|
||||
resourceUsers: ({ resourceId }: { resourceId: number }) =>
|
||||
queryOptions({
|
||||
queryKey: ["RESOURCES", resourceId, "USERS"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
@@ -240,7 +241,7 @@ export const resourceQueries = {
|
||||
return res.data.data.users;
|
||||
}
|
||||
}),
|
||||
resourceRoles: ({ resourceId }: { resourceId: number; }) =>
|
||||
resourceRoles: ({ resourceId }: { resourceId: number }) =>
|
||||
queryOptions({
|
||||
queryKey: ["RESOURCES", resourceId, "ROLES"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
@@ -251,7 +252,7 @@ export const resourceQueries = {
|
||||
return res.data.data.roles;
|
||||
}
|
||||
}),
|
||||
siteResourceUsers: ({ siteResourceId }: { siteResourceId: number; }) =>
|
||||
siteResourceUsers: ({ siteResourceId }: { siteResourceId: number }) =>
|
||||
queryOptions({
|
||||
queryKey: ["SITE_RESOURCES", siteResourceId, "USERS"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
@@ -261,7 +262,7 @@ export const resourceQueries = {
|
||||
return res.data.data.users;
|
||||
}
|
||||
}),
|
||||
siteResourceRoles: ({ siteResourceId }: { siteResourceId: number; }) =>
|
||||
siteResourceRoles: ({ siteResourceId }: { siteResourceId: number }) =>
|
||||
queryOptions({
|
||||
queryKey: ["SITE_RESOURCES", siteResourceId, "ROLES"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
@@ -272,7 +273,7 @@ export const resourceQueries = {
|
||||
return res.data.data.roles;
|
||||
}
|
||||
}),
|
||||
siteResourceClients: ({ siteResourceId }: { siteResourceId: number; }) =>
|
||||
siteResourceClients: ({ siteResourceId }: { siteResourceId: number }) =>
|
||||
queryOptions({
|
||||
queryKey: ["SITE_RESOURCES", siteResourceId, "CLIENTS"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
@@ -283,7 +284,7 @@ export const resourceQueries = {
|
||||
return res.data.data.clients;
|
||||
}
|
||||
}),
|
||||
resourceTargets: ({ resourceId }: { resourceId: number; }) =>
|
||||
resourceTargets: ({ resourceId }: { resourceId: number }) =>
|
||||
queryOptions({
|
||||
queryKey: ["RESOURCES", resourceId, "TARGETS"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
@@ -294,7 +295,7 @@ export const resourceQueries = {
|
||||
return res.data.data.targets;
|
||||
}
|
||||
}),
|
||||
resourceWhitelist: ({ resourceId }: { resourceId: number; }) =>
|
||||
resourceWhitelist: ({ resourceId }: { resourceId: number }) =>
|
||||
queryOptions({
|
||||
queryKey: ["RESOURCES", resourceId, "WHITELISTS"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
@@ -367,7 +368,7 @@ export const approvalQueries = {
|
||||
}
|
||||
|
||||
const res = await meta!.api.get<
|
||||
AxiosResponse<{ approvals: ApprovalItem[]; }>
|
||||
AxiosResponse<{ approvals: ApprovalItem[] }>
|
||||
>(`/org/${orgId}/approvals?${sp.toString()}`, {
|
||||
signal
|
||||
});
|
||||
@@ -379,7 +380,7 @@ export const approvalQueries = {
|
||||
queryKey: ["APPROVALS", orgId, "COUNT", "pending"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
const res = await meta!.api.get<
|
||||
AxiosResponse<{ count: number; }>
|
||||
AxiosResponse<{ count: number }>
|
||||
>(`/org/${orgId}/approvals/count?approvalState=pending`, {
|
||||
signal
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user