mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-28 07:46:36 +00:00
Compare commits
7 Commits
1.16.0-rc.
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad9289e0c1 | ||
|
|
8347203bbe | ||
|
|
4aa1186aed | ||
|
|
0d63a15715 | ||
|
|
fa2e229ada | ||
|
|
5d9700d84c | ||
|
|
f8a8cdaa5f |
@@ -1,4 +1,3 @@
|
|||||||
<<<<<<< HEAD
|
|
||||||
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
||||||
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
||||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
@@ -70,17 +69,12 @@ golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
|||||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
=======
|
|
||||||
>>>>>>> main
|
|
||||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||||
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||||
<<<<<<< HEAD
|
|
||||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
=======
|
|
||||||
>>>>>>> main
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|||||||
@@ -119,12 +119,12 @@ const listClientsSchema = z.object({
|
|||||||
}),
|
}),
|
||||||
query: z.string().optional(),
|
query: z.string().optional(),
|
||||||
sort_by: z
|
sort_by: z
|
||||||
.enum(["megabytesIn", "megabytesOut"])
|
.enum(["name", "megabytesIn", "megabytesOut"])
|
||||||
.optional()
|
.optional()
|
||||||
.catch(undefined)
|
.catch(undefined)
|
||||||
.openapi({
|
.openapi({
|
||||||
type: "string",
|
type: "string",
|
||||||
enum: ["megabytesIn", "megabytesOut"],
|
enum: ["name", "megabytesIn", "megabytesOut"],
|
||||||
description: "Field to sort by"
|
description: "Field to sort by"
|
||||||
}),
|
}),
|
||||||
order: z
|
order: z
|
||||||
@@ -363,14 +363,14 @@ export async function listClients(
|
|||||||
const countQuery = db.$count(baseQuery.as("filtered_clients"));
|
const countQuery = db.$count(baseQuery.as("filtered_clients"));
|
||||||
|
|
||||||
const listMachinesQuery = baseQuery
|
const listMachinesQuery = baseQuery
|
||||||
.limit(page)
|
.limit(pageSize)
|
||||||
.offset(pageSize * (page - 1))
|
.offset(pageSize * (page - 1))
|
||||||
.orderBy(
|
.orderBy(
|
||||||
sort_by
|
sort_by
|
||||||
? order === "asc"
|
? order === "asc"
|
||||||
? asc(clients[sort_by])
|
? asc(clients[sort_by])
|
||||||
: desc(clients[sort_by])
|
: desc(clients[sort_by])
|
||||||
: asc(clients.clientId)
|
: asc(clients.name)
|
||||||
);
|
);
|
||||||
|
|
||||||
const [clientsList, totalCount] = await Promise.all([
|
const [clientsList, totalCount] = await Promise.all([
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
and,
|
and,
|
||||||
asc,
|
asc,
|
||||||
count,
|
count,
|
||||||
|
desc,
|
||||||
eq,
|
eq,
|
||||||
inArray,
|
inArray,
|
||||||
isNull,
|
isNull,
|
||||||
@@ -63,6 +64,26 @@ const listResourcesSchema = z.object({
|
|||||||
description: "Page number to retrieve"
|
description: "Page number to retrieve"
|
||||||
}),
|
}),
|
||||||
query: z.string().optional(),
|
query: z.string().optional(),
|
||||||
|
sort_by: z
|
||||||
|
.enum(["name"])
|
||||||
|
.optional()
|
||||||
|
.catch(undefined)
|
||||||
|
.openapi({
|
||||||
|
type: "string",
|
||||||
|
enum: ["name"],
|
||||||
|
description: "Field to sort by"
|
||||||
|
}),
|
||||||
|
order: z
|
||||||
|
.enum(["asc", "desc"])
|
||||||
|
.optional()
|
||||||
|
.default("asc")
|
||||||
|
.catch("asc")
|
||||||
|
.openapi({
|
||||||
|
type: "string",
|
||||||
|
enum: ["asc", "desc"],
|
||||||
|
default: "asc",
|
||||||
|
description: "Sort order"
|
||||||
|
}),
|
||||||
enabled: z
|
enabled: z
|
||||||
.enum(["true", "false"])
|
.enum(["true", "false"])
|
||||||
.transform((v) => v === "true")
|
.transform((v) => v === "true")
|
||||||
@@ -229,8 +250,16 @@ export async function listResources(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const { page, pageSize, authState, enabled, query, healthStatus } =
|
const {
|
||||||
parsedQuery.data;
|
page,
|
||||||
|
pageSize,
|
||||||
|
authState,
|
||||||
|
enabled,
|
||||||
|
query,
|
||||||
|
healthStatus,
|
||||||
|
sort_by,
|
||||||
|
order
|
||||||
|
} = parsedQuery.data;
|
||||||
|
|
||||||
const parsedParams = listResourcesParamsSchema.safeParse(req.params);
|
const parsedParams = listResourcesParamsSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
@@ -395,7 +424,13 @@ export async function listResources(
|
|||||||
baseQuery
|
baseQuery
|
||||||
.limit(pageSize)
|
.limit(pageSize)
|
||||||
.offset(pageSize * (page - 1))
|
.offset(pageSize * (page - 1))
|
||||||
.orderBy(asc(resources.resourceId)),
|
.orderBy(
|
||||||
|
sort_by
|
||||||
|
? order === "asc"
|
||||||
|
? asc(resources[sort_by])
|
||||||
|
: desc(resources[sort_by])
|
||||||
|
: asc(resources.name)
|
||||||
|
),
|
||||||
countQuery
|
countQuery
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -108,12 +108,12 @@ const listSitesSchema = z.object({
|
|||||||
}),
|
}),
|
||||||
query: z.string().optional(),
|
query: z.string().optional(),
|
||||||
sort_by: z
|
sort_by: z
|
||||||
.enum(["megabytesIn", "megabytesOut"])
|
.enum(["name", "megabytesIn", "megabytesOut"])
|
||||||
.optional()
|
.optional()
|
||||||
.catch(undefined)
|
.catch(undefined)
|
||||||
.openapi({
|
.openapi({
|
||||||
type: "string",
|
type: "string",
|
||||||
enum: ["megabytesIn", "megabytesOut"],
|
enum: ["name", "megabytesIn", "megabytesOut"],
|
||||||
description: "Field to sort by"
|
description: "Field to sort by"
|
||||||
}),
|
}),
|
||||||
order: z
|
order: z
|
||||||
@@ -289,7 +289,7 @@ export async function listSites(
|
|||||||
? order === "asc"
|
? order === "asc"
|
||||||
? asc(sites[sort_by])
|
? asc(sites[sort_by])
|
||||||
: desc(sites[sort_by])
|
: desc(sites[sort_by])
|
||||||
: asc(sites.siteId)
|
: asc(sites.name)
|
||||||
);
|
);
|
||||||
|
|
||||||
const [totalCount, rows] = await Promise.all([
|
const [totalCount, rows] = await Promise.all([
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import logger from "@server/logger";
|
|||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import type { PaginatedResponse } from "@server/types/Pagination";
|
import type { PaginatedResponse } from "@server/types/Pagination";
|
||||||
import { and, asc, eq, like, or, sql } from "drizzle-orm";
|
import { and, asc, desc, eq, like, or, sql } from "drizzle-orm";
|
||||||
import { NextFunction, Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -48,6 +48,26 @@ const listAllSiteResourcesByOrgQuerySchema = z.object({
|
|||||||
type: "string",
|
type: "string",
|
||||||
enum: ["host", "cidr"],
|
enum: ["host", "cidr"],
|
||||||
description: "Filter site resources by mode"
|
description: "Filter site resources by mode"
|
||||||
|
}),
|
||||||
|
sort_by: z
|
||||||
|
.enum(["name"])
|
||||||
|
.optional()
|
||||||
|
.catch(undefined)
|
||||||
|
.openapi({
|
||||||
|
type: "string",
|
||||||
|
enum: ["name"],
|
||||||
|
description: "Field to sort by"
|
||||||
|
}),
|
||||||
|
order: z
|
||||||
|
.enum(["asc", "desc"])
|
||||||
|
.optional()
|
||||||
|
.default("asc")
|
||||||
|
.catch("asc")
|
||||||
|
.openapi({
|
||||||
|
type: "string",
|
||||||
|
enum: ["asc", "desc"],
|
||||||
|
default: "asc",
|
||||||
|
description: "Sort order"
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -131,7 +151,8 @@ export async function listAllSiteResourcesByOrg(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { orgId } = parsedParams.data;
|
const { orgId } = parsedParams.data;
|
||||||
const { page, pageSize, query, mode } = parsedQuery.data;
|
const { page, pageSize, query, mode, sort_by, order } =
|
||||||
|
parsedQuery.data;
|
||||||
|
|
||||||
const conditions = [and(eq(siteResources.orgId, orgId))];
|
const conditions = [and(eq(siteResources.orgId, orgId))];
|
||||||
if (query) {
|
if (query) {
|
||||||
@@ -179,7 +200,13 @@ export async function listAllSiteResourcesByOrg(
|
|||||||
baseQuery
|
baseQuery
|
||||||
.limit(pageSize)
|
.limit(pageSize)
|
||||||
.offset(pageSize * (page - 1))
|
.offset(pageSize * (page - 1))
|
||||||
.orderBy(asc(siteResources.siteResourceId)),
|
.orderBy(
|
||||||
|
sort_by
|
||||||
|
? order === "asc"
|
||||||
|
? asc(siteResources[sort_by])
|
||||||
|
: desc(siteResources[sort_by])
|
||||||
|
: asc(siteResources.name)
|
||||||
|
),
|
||||||
countQuery
|
countQuery
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { siteResources, sites, SiteResource } from "@server/db";
|
|||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { and, asc, desc, eq } from "drizzle-orm";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
@@ -27,7 +27,16 @@ const listSiteResourcesQuerySchema = z.object({
|
|||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.int().nonnegative())
|
.pipe(z.int().nonnegative()),
|
||||||
|
sort_by: z
|
||||||
|
.enum(["name"])
|
||||||
|
.optional()
|
||||||
|
.catch(undefined),
|
||||||
|
order: z
|
||||||
|
.enum(["asc", "desc"])
|
||||||
|
.optional()
|
||||||
|
.default("asc")
|
||||||
|
.catch("asc")
|
||||||
});
|
});
|
||||||
|
|
||||||
export type ListSiteResourcesResponse = {
|
export type ListSiteResourcesResponse = {
|
||||||
@@ -75,7 +84,7 @@ export async function listSiteResources(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { siteId, orgId } = parsedParams.data;
|
const { siteId, orgId } = parsedParams.data;
|
||||||
const { limit, offset } = parsedQuery.data;
|
const { limit, offset, sort_by, order } = parsedQuery.data;
|
||||||
|
|
||||||
// Verify the site exists and belongs to the org
|
// Verify the site exists and belongs to the org
|
||||||
const site = await db
|
const site = await db
|
||||||
@@ -98,6 +107,13 @@ export async function listSiteResources(
|
|||||||
eq(siteResources.orgId, orgId)
|
eq(siteResources.orgId, orgId)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.orderBy(
|
||||||
|
sort_by
|
||||||
|
? order === "asc"
|
||||||
|
? asc(siteResources[sort_by])
|
||||||
|
: desc(siteResources[sort_by])
|
||||||
|
: asc(siteResources.name)
|
||||||
|
)
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
.offset(offset);
|
.offset(offset);
|
||||||
|
|
||||||
|
|||||||
@@ -210,7 +210,8 @@ export default function BillingPage() {
|
|||||||
({ subscription }) =>
|
({ subscription }) =>
|
||||||
subscription?.type === "tier1" ||
|
subscription?.type === "tier1" ||
|
||||||
subscription?.type === "tier2" ||
|
subscription?.type === "tier2" ||
|
||||||
subscription?.type === "tier3"
|
subscription?.type === "tier3" ||
|
||||||
|
subscription?.type === "enterprise"
|
||||||
);
|
);
|
||||||
setTierSubscription(tierSub || null);
|
setTierSubscription(tierSub || null);
|
||||||
|
|
||||||
@@ -439,6 +440,8 @@ export default function BillingPage() {
|
|||||||
// Get current plan ID from tier
|
// Get current plan ID from tier
|
||||||
const getCurrentPlanId = (): PlanId => {
|
const getCurrentPlanId = (): PlanId => {
|
||||||
if (!hasSubscription || !currentTier) return "basic";
|
if (!hasSubscription || !currentTier) return "basic";
|
||||||
|
// Handle enterprise subscription type directly
|
||||||
|
if (currentTier === "enterprise") return "enterprise";
|
||||||
const plan = planOptions.find((p) => p.tierType === currentTier);
|
const plan = planOptions.find((p) => p.tierType === currentTier);
|
||||||
return plan?.id || "basic";
|
return plan?.id || "basic";
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import { redirect } from "next/navigation";
|
|||||||
import DeviceLoginForm from "@/components/DeviceLoginForm";
|
import DeviceLoginForm from "@/components/DeviceLoginForm";
|
||||||
import { getUserDisplayName } from "@app/lib/getUserDisplayName";
|
import { getUserDisplayName } from "@app/lib/getUserDisplayName";
|
||||||
import { cache } from "react";
|
import { cache } from "react";
|
||||||
|
import { cleanRedirect } from "@app/lib/cleanRedirect";
|
||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
searchParams: Promise<{ code?: string; user?: string }>;
|
searchParams: Promise<{ code?: string; user?: string; authPath?: string }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
function deviceRedirectSearchParams(params: {
|
function deviceRedirectSearchParams(params: {
|
||||||
@@ -30,11 +31,11 @@ export default async function DeviceLoginPage({ searchParams }: Props) {
|
|||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
const redirectDestination = `/auth/login/device${deviceRedirectSearchParams({ code, user: params.user })}`;
|
const redirectDestination = `/auth/login/device${deviceRedirectSearchParams({ code, user: params.user })}`;
|
||||||
const loginUrl = new URL("/auth/login", "http://x");
|
const authPath = cleanRedirect(params.authPath || "/auth/login");
|
||||||
|
const loginUrl = new URL(authPath, "http://x");
|
||||||
loginUrl.searchParams.set("forceLogin", "true");
|
loginUrl.searchParams.set("forceLogin", "true");
|
||||||
loginUrl.searchParams.set("redirect", redirectDestination);
|
loginUrl.searchParams.set("redirect", redirectDestination);
|
||||||
if (defaultUser) loginUrl.searchParams.set("user", defaultUser);
|
if (defaultUser) loginUrl.searchParams.set("user", defaultUser);
|
||||||
console.log("loginUrl", loginUrl.pathname + loginUrl.search);
|
|
||||||
redirect(loginUrl.pathname + loginUrl.search);
|
redirect(loginUrl.pathname + loginUrl.search);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,15 @@ import { InfoPopup } from "@app/components/ui/info-popup";
|
|||||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
import { toast } from "@app/hooks/useToast";
|
import { toast } from "@app/hooks/useToast";
|
||||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||||
import { ArrowUpDown, ArrowUpRight, MoreHorizontal } from "lucide-react";
|
import { getNextSortOrder, getSortDirection } from "@app/lib/sortColumn";
|
||||||
|
import {
|
||||||
|
ArrowDown01Icon,
|
||||||
|
ArrowUp10Icon,
|
||||||
|
ArrowUpDown,
|
||||||
|
ArrowUpRight,
|
||||||
|
ChevronsUpDownIcon,
|
||||||
|
MoreHorizontal
|
||||||
|
} from "lucide-react";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
@@ -133,7 +141,26 @@ export default function ClientResourcesTable({
|
|||||||
accessorKey: "name",
|
accessorKey: "name",
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
friendlyName: t("name"),
|
friendlyName: t("name"),
|
||||||
header: () => <span className="p-3">{t("name")}</span>
|
header: () => {
|
||||||
|
const nameOrder = getSortDirection("name", searchParams);
|
||||||
|
const Icon =
|
||||||
|
nameOrder === "asc"
|
||||||
|
? ArrowDown01Icon
|
||||||
|
: nameOrder === "desc"
|
||||||
|
? ArrowUp10Icon
|
||||||
|
: ChevronsUpDownIcon;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
className="p-3"
|
||||||
|
onClick={() => toggleSort("name")}
|
||||||
|
>
|
||||||
|
{t("name")}
|
||||||
|
<Icon className="ml-2 h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "niceId",
|
id: "niceId",
|
||||||
@@ -329,6 +356,14 @@ export default function ClientResourcesTable({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleSort(column: string) {
|
||||||
|
const newSearch = getNextSortOrder(column, searchParams);
|
||||||
|
|
||||||
|
filter({
|
||||||
|
searchParams: newSearch
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const handlePaginationChange = (newPage: PaginationState) => {
|
const handlePaginationChange = (newPage: PaginationState) => {
|
||||||
searchParams.set("page", (newPage.pageIndex + 1).toString());
|
searchParams.set("page", (newPage.pageIndex + 1).toString());
|
||||||
searchParams.set("pageSize", newPage.pageSize.toString());
|
searchParams.set("pageSize", newPage.pageSize.toString());
|
||||||
|
|||||||
@@ -204,7 +204,26 @@ export default function MachineClientsTable({
|
|||||||
accessorKey: "name",
|
accessorKey: "name",
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
friendlyName: t("name"),
|
friendlyName: t("name"),
|
||||||
header: () => <span className="px-3">{t("name")}</span>,
|
header: () => {
|
||||||
|
const nameOrder = getSortDirection("name", searchParams);
|
||||||
|
const Icon =
|
||||||
|
nameOrder === "asc"
|
||||||
|
? ArrowDown01Icon
|
||||||
|
: nameOrder === "desc"
|
||||||
|
? ArrowUp10Icon
|
||||||
|
: ChevronsUpDownIcon;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => toggleSort("name")}
|
||||||
|
className="px-3"
|
||||||
|
>
|
||||||
|
{t("name")}
|
||||||
|
<Icon className="ml-2 h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
},
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const r = row.original;
|
const r = row.original;
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -14,15 +14,19 @@ import { InfoPopup } from "@app/components/ui/info-popup";
|
|||||||
import { Switch } from "@app/components/ui/switch";
|
import { Switch } from "@app/components/ui/switch";
|
||||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
import { useNavigationContext } from "@app/hooks/useNavigationContext";
|
import { useNavigationContext } from "@app/hooks/useNavigationContext";
|
||||||
|
import { getNextSortOrder, getSortDirection } from "@app/lib/sortColumn";
|
||||||
import { toast } from "@app/hooks/useToast";
|
import { toast } from "@app/hooks/useToast";
|
||||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||||
import { UpdateResourceResponse } from "@server/routers/resource";
|
import { UpdateResourceResponse } from "@server/routers/resource";
|
||||||
import type { PaginationState } from "@tanstack/react-table";
|
import type { PaginationState } from "@tanstack/react-table";
|
||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import {
|
import {
|
||||||
|
ArrowDown01Icon,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
|
ArrowUp10Icon,
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
|
ChevronsUpDownIcon,
|
||||||
Clock,
|
Clock,
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
@@ -318,7 +322,26 @@ export default function ProxyResourcesTable({
|
|||||||
accessorKey: "name",
|
accessorKey: "name",
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
friendlyName: t("name"),
|
friendlyName: t("name"),
|
||||||
header: () => <span className="p-3">{t("name")}</span>
|
header: () => {
|
||||||
|
const nameOrder = getSortDirection("name", searchParams);
|
||||||
|
const Icon =
|
||||||
|
nameOrder === "asc"
|
||||||
|
? ArrowDown01Icon
|
||||||
|
: nameOrder === "desc"
|
||||||
|
? ArrowUp10Icon
|
||||||
|
: ChevronsUpDownIcon;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
className="p-3"
|
||||||
|
onClick={() => toggleSort("name")}
|
||||||
|
>
|
||||||
|
{t("name")}
|
||||||
|
<Icon className="ml-2 h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "niceId",
|
id: "niceId",
|
||||||
@@ -563,6 +586,14 @@ export default function ProxyResourcesTable({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleSort(column: string) {
|
||||||
|
const newSearch = getNextSortOrder(column, searchParams);
|
||||||
|
|
||||||
|
filter({
|
||||||
|
searchParams: newSearch
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const handlePaginationChange = (newPage: PaginationState) => {
|
const handlePaginationChange = (newPage: PaginationState) => {
|
||||||
searchParams.set("page", (newPage.pageIndex + 1).toString());
|
searchParams.set("page", (newPage.pageIndex + 1).toString());
|
||||||
searchParams.set("pageSize", newPage.pageSize.toString());
|
searchParams.set("pageSize", newPage.pageSize.toString());
|
||||||
|
|||||||
@@ -141,7 +141,24 @@ export default function SitesTable({
|
|||||||
accessorKey: "name",
|
accessorKey: "name",
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
header: () => {
|
header: () => {
|
||||||
return <span className="p-3">{t("name")}</span>;
|
const nameOrder = getSortDirection("name", searchParams);
|
||||||
|
const Icon =
|
||||||
|
nameOrder === "asc"
|
||||||
|
? ArrowDown01Icon
|
||||||
|
: nameOrder === "desc"
|
||||||
|
? ArrowUp10Icon
|
||||||
|
: ChevronsUpDownIcon;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
className="p-3"
|
||||||
|
onClick={() => toggleSort("name")}
|
||||||
|
>
|
||||||
|
{t("name")}
|
||||||
|
<Icon className="ml-2 h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user