Merge pull request #1960 from Fredkiss3/refactor/separate-tables

refactor: separate tables
This commit is contained in:
Milo Schwartz
2025-12-03 11:28:21 -08:00
committed by GitHub
31 changed files with 4383 additions and 3229 deletions

View File

@@ -1,17 +1,15 @@
import { verifySession } from "@app/lib/auth/verifySession";
import UserProvider from "@app/providers/UserProvider";
import { cache } from "react";
import MemberResourcesPortal from "../../components/MemberResourcesPortal";
import { GetOrgOverviewResponse } from "@server/routers/org/getOrgOverview";
import { internal } from "@app/lib/api";
import { AxiosResponse } from "axios";
import { authCookieHeader } from "@app/lib/api/cookies";
import { redirect } from "next/navigation";
import { Layout } from "@app/components/Layout";
import { ListUserOrgsResponse } from "@server/routers/org";
import MemberResourcesPortal from "@app/components/MemberResourcesPortal";
import { internal } from "@app/lib/api";
import { authCookieHeader } from "@app/lib/api/cookies";
import { verifySession } from "@app/lib/auth/verifySession";
import { pullEnv } from "@app/lib/pullEnv";
import EnvProvider from "@app/providers/EnvProvider";
import { orgLangingNavItems } from "@app/app/navigation";
import UserProvider from "@app/providers/UserProvider";
import { ListUserOrgsResponse } from "@server/routers/org";
import { GetOrgOverviewResponse } from "@server/routers/org/getOrgOverview";
import { AxiosResponse } from "axios";
import { redirect } from "next/navigation";
import { cache } from "react";
type OrgPageProps = {
params: Promise<{ orgId: string }>;

View File

@@ -0,0 +1,78 @@
import type { ClientRow } from "@app/components/MachineClientsTable";
import MachineClientsTable from "@app/components/MachineClientsTable";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { internal } from "@app/lib/api";
import { authCookieHeader } from "@app/lib/api/cookies";
import { ListClientsResponse } from "@server/routers/client";
import { AxiosResponse } from "axios";
import { getTranslations } from "next-intl/server";
type ClientsPageProps = {
params: Promise<{ orgId: string }>;
searchParams: Promise<{ view?: string }>;
};
export const dynamic = "force-dynamic";
export default async function ClientsPage(props: ClientsPageProps) {
const t = await getTranslations();
const params = await props.params;
let machineClients: ListClientsResponse["clients"] = [];
try {
const machineRes = await internal.get<
AxiosResponse<ListClientsResponse>
>(
`/org/${params.orgId}/clients?filter=machine`,
await authCookieHeader()
);
machineClients = machineRes.data.data.clients;
} catch (e) {}
function formatSize(mb: number): string {
if (mb >= 1024 * 1024) {
return `${(mb / (1024 * 1024)).toFixed(2)} TB`;
} else if (mb >= 1024) {
return `${(mb / 1024).toFixed(2)} GB`;
} else {
return `${mb.toFixed(2)} MB`;
}
}
const mapClientToRow = (
client: ListClientsResponse["clients"][0]
): ClientRow => {
return {
name: client.name,
id: client.clientId,
subnet: client.subnet.split("/")[0],
mbIn: formatSize(client.megabytesIn || 0),
mbOut: formatSize(client.megabytesOut || 0),
orgId: params.orgId,
online: client.online,
olmVersion: client.olmVersion || undefined,
olmUpdateAvailable: client.olmUpdateAvailable || false,
userId: client.userId,
username: client.username,
userEmail: client.userEmail
};
};
const machineClientRows: ClientRow[] = machineClients.map(mapClientToRow);
return (
<>
<SettingsSectionTitle
title={t("manageMachineClients")}
description={t("manageMachineClientsDescription")}
/>
<MachineClientsTable
machineClients={machineClientRows}
orgId={params.orgId}
/>
</>
);
}

View File

@@ -1,11 +1,4 @@
import { internal } from "@app/lib/api";
import { authCookieHeader } from "@app/lib/api/cookies";
import { AxiosResponse } from "axios";
import { ClientRow } from "../../../../components/ClientsTable";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { ListClientsResponse } from "@server/routers/client";
import ClientsTable from "../../../../components/ClientsTable";
import { getTranslations } from "next-intl/server";
import { redirect } from "next/navigation";
type ClientsPageProps = {
params: Promise<{ orgId: string }>;
@@ -15,76 +8,6 @@ type ClientsPageProps = {
export const dynamic = "force-dynamic";
export default async function ClientsPage(props: ClientsPageProps) {
const t = await getTranslations();
const params = await props.params;
const searchParams = await props.searchParams;
// Default to 'user' view, or use the query param if provided
let defaultView: "user" | "machine" = "user";
defaultView = searchParams.view === "machine" ? "machine" : "user";
let userClients: ListClientsResponse["clients"] = [];
let machineClients: ListClientsResponse["clients"] = [];
try {
const [userRes, machineRes] = await Promise.all([
internal.get<AxiosResponse<ListClientsResponse>>(
`/org/${params.orgId}/clients?filter=user`,
await authCookieHeader()
),
internal.get<AxiosResponse<ListClientsResponse>>(
`/org/${params.orgId}/clients?filter=machine`,
await authCookieHeader()
)
]);
userClients = userRes.data.data.clients;
machineClients = machineRes.data.data.clients;
} catch (e) {}
function formatSize(mb: number): string {
if (mb >= 1024 * 1024) {
return `${(mb / (1024 * 1024)).toFixed(2)} TB`;
} else if (mb >= 1024) {
return `${(mb / 1024).toFixed(2)} GB`;
} else {
return `${mb.toFixed(2)} MB`;
}
}
const mapClientToRow = (client: ListClientsResponse["clients"][0]): ClientRow => {
return {
name: client.name,
id: client.clientId,
subnet: client.subnet.split("/")[0],
mbIn: formatSize(client.megabytesIn || 0),
mbOut: formatSize(client.megabytesOut || 0),
orgId: params.orgId,
online: client.online,
olmVersion: client.olmVersion || undefined,
olmUpdateAvailable: client.olmUpdateAvailable || false,
userId: client.userId,
username: client.username,
userEmail: client.userEmail
};
};
const userClientRows: ClientRow[] = userClients.map(mapClientToRow);
const machineClientRows: ClientRow[] = machineClients.map(mapClientToRow);
return (
<>
<SettingsSectionTitle
title={t("manageClients")}
description={t("manageClientsDescription")}
/>
<ClientsTable
userClients={userClientRows}
machineClients={machineClientRows}
orgId={params.orgId}
defaultView={defaultView}
/>
</>
);
redirect(`/${params.orgId}/settings/clients/user`);
}

View File

@@ -0,0 +1,75 @@
import { internal } from "@app/lib/api";
import { authCookieHeader } from "@app/lib/api/cookies";
import { AxiosResponse } from "axios";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { ListClientsResponse } from "@server/routers/client";
import { getTranslations } from "next-intl/server";
import type { ClientRow } from "@app/components/MachineClientsTable";
import UserDevicesTable from "@app/components/UserDevicesTable";
type ClientsPageProps = {
params: Promise<{ orgId: string }>;
};
export const dynamic = "force-dynamic";
export default async function ClientsPage(props: ClientsPageProps) {
const t = await getTranslations();
const params = await props.params;
let userClients: ListClientsResponse["clients"] = [];
try {
const userRes = await internal.get<AxiosResponse<ListClientsResponse>>(
`/org/${params.orgId}/clients?filter=user`,
await authCookieHeader()
);
userClients = userRes.data.data.clients;
} catch (e) {}
function formatSize(mb: number): string {
if (mb >= 1024 * 1024) {
return `${(mb / (1024 * 1024)).toFixed(2)} TB`;
} else if (mb >= 1024) {
return `${(mb / 1024).toFixed(2)} GB`;
} else {
return `${mb.toFixed(2)} MB`;
}
}
const mapClientToRow = (
client: ListClientsResponse["clients"][0]
): ClientRow => {
return {
name: client.name,
id: client.clientId,
subnet: client.subnet.split("/")[0],
mbIn: formatSize(client.megabytesIn || 0),
mbOut: formatSize(client.megabytesOut || 0),
orgId: params.orgId,
online: client.online,
olmVersion: client.olmVersion || undefined,
olmUpdateAvailable: client.olmUpdateAvailable || false,
userId: client.userId,
username: client.username,
userEmail: client.userEmail
};
};
const userClientRows: ClientRow[] = userClients.map(mapClientToRow);
return (
<>
<SettingsSectionTitle
title={t("manageUserDevices")}
description={t("manageUserDevicesDescription")}
/>
<UserDevicesTable
userClients={userClientRows}
orgId={params.orgId}
/>
</>
);
}

View File

@@ -0,0 +1,93 @@
import ClientResourcesTable from "@app/components/ClientResourcesTable";
import type { InternalResourceRow } from "@app/components/ClientResourcesTable";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { internal } from "@app/lib/api";
import { authCookieHeader } from "@app/lib/api/cookies";
import { getCachedOrg } from "@app/lib/api/getCachedOrg";
import OrgProvider from "@app/providers/OrgProvider";
import type { ListResourcesResponse } from "@server/routers/resource";
import type { ListAllSiteResourcesByOrgResponse } from "@server/routers/siteResource";
import type { AxiosResponse } from "axios";
import { getTranslations } from "next-intl/server";
import { redirect } from "next/navigation";
import { cache } from "react";
export interface ClientResourcesPageProps {
params: Promise<{ orgId: string }>;
searchParams: Promise<{ view?: string }>;
}
export default async function ClientResourcesPage(
props: ClientResourcesPageProps
) {
const params = await props.params;
const t = await getTranslations();
let resources: ListResourcesResponse["resources"] = [];
try {
const res = await internal.get<AxiosResponse<ListResourcesResponse>>(
`/org/${params.orgId}/resources`,
await authCookieHeader()
);
resources = res.data.data.resources;
} catch (e) {}
let siteResources: ListAllSiteResourcesByOrgResponse["siteResources"] = [];
try {
const res = await internal.get<
AxiosResponse<ListAllSiteResourcesByOrgResponse>
>(`/org/${params.orgId}/site-resources`, await authCookieHeader());
siteResources = res.data.data.siteResources;
} catch (e) {}
let org = null;
try {
const res = await getCachedOrg(params.orgId);
org = res.data.data;
} catch {
redirect(`/${params.orgId}/settings/resources`);
}
if (!org) {
redirect(`/${params.orgId}/settings/resources`);
}
const internalResourceRows: InternalResourceRow[] = siteResources.map(
(siteResource) => {
return {
id: siteResource.siteResourceId,
name: siteResource.name,
orgId: params.orgId,
siteName: siteResource.siteName,
siteAddress: siteResource.siteAddress || null,
mode: siteResource.mode || ("port" as any),
// protocol: siteResource.protocol,
// proxyPort: siteResource.proxyPort,
siteId: siteResource.siteId,
destination: siteResource.destination,
// destinationPort: siteResource.destinationPort,
alias: siteResource.alias || null,
siteNiceId: siteResource.siteNiceId
};
}
);
return (
<>
<SettingsSectionTitle
title={t("clientResourceTitle")}
description={t("clientResourceDescription")}
/>
<OrgProvider org={org}>
<ClientResourcesTable
internalResources={internalResourceRows}
orgId={params.orgId}
defaultSort={{
id: "name",
desc: false
}}
/>
</OrgProvider>
</>
);
}

View File

@@ -1,149 +1,10 @@
import { internal } from "@app/lib/api";
import { authCookieHeader } from "@app/lib/api/cookies";
import ResourcesTable, {
ResourceRow,
InternalResourceRow
} from "../../../../components/ResourcesTable";
import { AxiosResponse } from "axios";
import { ListResourcesResponse } from "@server/routers/resource";
import { ListAllSiteResourcesByOrgResponse } from "@server/routers/siteResource";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { redirect } from "next/navigation";
import { cache } from "react";
import { GetOrgResponse } from "@server/routers/org";
import OrgProvider from "@app/providers/OrgProvider";
import { getTranslations } from "next-intl/server";
import { pullEnv } from "@app/lib/pullEnv";
import { toUnicode } from "punycode";
type ResourcesPageProps = {
export interface ResourcesPageProps {
params: Promise<{ orgId: string }>;
searchParams: Promise<{ view?: string }>;
};
export const dynamic = "force-dynamic";
}
export default async function ResourcesPage(props: ResourcesPageProps) {
const params = await props.params;
const searchParams = await props.searchParams;
const t = await getTranslations();
const env = pullEnv();
// Default to 'proxy' view, or use the query param if provided
let defaultView: "proxy" | "internal" = "proxy";
if (env.flags.enableClients) {
defaultView = searchParams.view === "internal" ? "internal" : "proxy";
}
let resources: ListResourcesResponse["resources"] = [];
try {
const res = await internal.get<AxiosResponse<ListResourcesResponse>>(
`/org/${params.orgId}/resources`,
await authCookieHeader()
);
resources = res.data.data.resources;
} catch (e) {}
let siteResources: ListAllSiteResourcesByOrgResponse["siteResources"] = [];
try {
const res = await internal.get<
AxiosResponse<ListAllSiteResourcesByOrgResponse>
>(`/org/${params.orgId}/site-resources`, await authCookieHeader());
siteResources = res.data.data.siteResources;
} catch (e) {}
let org = null;
try {
const getOrg = cache(async () =>
internal.get<AxiosResponse<GetOrgResponse>>(
`/org/${params.orgId}`,
await authCookieHeader()
)
);
const res = await getOrg();
org = res.data.data;
} catch {
redirect(`/${params.orgId}/settings/resources`);
}
if (!org) {
redirect(`/${params.orgId}/settings/resources`);
}
const resourceRows: ResourceRow[] = resources.map((resource) => {
return {
id: resource.resourceId,
name: resource.name,
orgId: params.orgId,
nice: resource.niceId,
domain: `${resource.ssl ? "https://" : "http://"}${toUnicode(resource.fullDomain || "")}`,
protocol: resource.protocol,
proxyPort: resource.proxyPort,
http: resource.http,
authState: !resource.http
? "none"
: resource.sso ||
resource.pincodeId !== null ||
resource.passwordId !== null ||
resource.whitelist ||
resource.headerAuthId
? "protected"
: "not_protected",
enabled: resource.enabled,
domainId: resource.domainId || undefined,
ssl: resource.ssl,
targets: resource.targets?.map((target) => ({
targetId: target.targetId,
ip: target.ip,
port: target.port,
enabled: target.enabled,
healthStatus: target.healthStatus
}))
};
});
const internalResourceRows: InternalResourceRow[] = siteResources.map(
(siteResource) => {
return {
id: siteResource.siteResourceId,
name: siteResource.name,
orgId: params.orgId,
siteName: siteResource.siteName,
siteAddress: siteResource.siteAddress || null,
mode: siteResource.mode || ("port" as any),
// protocol: siteResource.protocol,
// proxyPort: siteResource.proxyPort,
siteId: siteResource.siteId,
destination: siteResource.destination,
// destinationPort: siteResource.destinationPort,
alias: siteResource.alias || null,
siteNiceId: siteResource.siteNiceId
};
}
);
return (
<>
<SettingsSectionTitle
title={t("resourceTitle")}
description={t("resourceDescription")}
/>
<OrgProvider org={org}>
<ResourcesTable
resources={resourceRows}
internalResources={internalResourceRows}
orgId={params.orgId}
defaultView={
env.flags.enableClients ? defaultView : "proxy"
}
defaultSort={{
id: "name",
desc: false
}}
/>
</OrgProvider>
</>
);
redirect(`/${params.orgId}/settings/resources/proxy`);
}

View File

@@ -0,0 +1,112 @@
import type { ResourceRow } from "@app/components/ProxyResourcesTable";
import ProxyResourcesTable from "@app/components/ProxyResourcesTable";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { internal } from "@app/lib/api";
import { authCookieHeader } from "@app/lib/api/cookies";
import OrgProvider from "@app/providers/OrgProvider";
import type { GetOrgResponse } from "@server/routers/org";
import type { ListResourcesResponse } from "@server/routers/resource";
import type { ListAllSiteResourcesByOrgResponse } from "@server/routers/siteResource";
import type { AxiosResponse } from "axios";
import { getTranslations } from "next-intl/server";
import { redirect } from "next/navigation";
import { toUnicode } from "punycode";
import { cache } from "react";
export interface ProxyResourcesPageProps {
params: Promise<{ orgId: string }>;
searchParams: Promise<{ view?: string }>;
}
export default async function ProxyResourcesPage(
props: ProxyResourcesPageProps
) {
const params = await props.params;
const t = await getTranslations();
let resources: ListResourcesResponse["resources"] = [];
try {
const res = await internal.get<AxiosResponse<ListResourcesResponse>>(
`/org/${params.orgId}/resources`,
await authCookieHeader()
);
resources = res.data.data.resources;
} catch (e) {}
let siteResources: ListAllSiteResourcesByOrgResponse["siteResources"] = [];
try {
const res = await internal.get<
AxiosResponse<ListAllSiteResourcesByOrgResponse>
>(`/org/${params.orgId}/site-resources`, await authCookieHeader());
siteResources = res.data.data.siteResources;
} catch (e) {}
let org = null;
try {
const getOrg = cache(async () =>
internal.get<AxiosResponse<GetOrgResponse>>(
`/org/${params.orgId}`,
await authCookieHeader()
)
);
const res = await getOrg();
org = res.data.data;
} catch {
redirect(`/${params.orgId}/settings/resources`);
}
if (!org) {
redirect(`/${params.orgId}/settings/resources`);
}
const resourceRows: ResourceRow[] = resources.map((resource) => {
return {
id: resource.resourceId,
name: resource.name,
orgId: params.orgId,
nice: resource.niceId,
domain: `${resource.ssl ? "https://" : "http://"}${toUnicode(resource.fullDomain || "")}`,
protocol: resource.protocol,
proxyPort: resource.proxyPort,
http: resource.http,
authState: !resource.http
? "none"
: resource.sso ||
resource.pincodeId !== null ||
resource.passwordId !== null ||
resource.whitelist ||
resource.headerAuthId
? "protected"
: "not_protected",
enabled: resource.enabled,
domainId: resource.domainId || undefined,
ssl: resource.ssl,
targets: resource.targets?.map((target) => ({
targetId: target.targetId,
ip: target.ip,
port: target.port,
enabled: target.enabled,
healthStatus: target.healthStatus
}))
};
});
return (
<>
<SettingsSectionTitle
title={t("proxyResourceTitle")}
description={t("proxyResourceDescription")}
/>
<OrgProvider org={org}>
<ProxyResourcesTable
resources={resourceRows}
orgId={params.orgId}
defaultSort={{
id: "name",
desc: false
}}
/>
</OrgProvider>
</>
);
}

View File

@@ -20,7 +20,7 @@ import { Toaster } from "@app/components/ui/toaster";
import { build } from "@server/build";
import { TopLoader } from "@app/components/Toploader";
import Script from "next/script";
import { ReactQueryProvider } from "@app/components/react-query-provider";
import { TanstackQueryProvider } from "@app/components/TanstackQueryProvider";
export const metadata: Metadata = {
title: `Dashboard - ${process.env.BRANDING_APP_NAME || "Pangolin"}`,
@@ -95,16 +95,16 @@ export default async function RootLayout({
strategy="afterInteractive"
/>
)}
<ReactQueryProvider>
<NextIntlClientProvider>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<ThemeDataProvider colors={loadBrandingColors()}>
<EnvProvider env={pullEnv()}>
<NextIntlClientProvider>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<ThemeDataProvider colors={loadBrandingColors()}>
<EnvProvider env={env}>
<TanstackQueryProvider>
<LicenseStatusProvider
licenseStatus={licenseStatus}
>
@@ -124,11 +124,11 @@ export default async function RootLayout({
</SupportStatusProvider>
</LicenseStatusProvider>
<Toaster />
</EnvProvider>
</ThemeDataProvider>
</ThemeProvider>
</NextIntlClientProvider>
</ReactQueryProvider>
</TanstackQueryProvider>
</EnvProvider>
</ThemeDataProvider>
</ThemeProvider>
</NextIntlClientProvider>
</body>
</html>
);

View File

@@ -17,7 +17,9 @@ import {
CreditCard,
Logs,
SquareMousePointer,
ScanEye
ScanEye,
GlobeLock,
Smartphone
} from "lucide-react";
export type SidebarNavSection = {
@@ -31,7 +33,7 @@ export const orgLangingNavItems: SidebarNavItem[] = [
{
title: "sidebarAccount",
href: "/{orgId}",
icon: <User className="h-4 w-4" />
icon: <User className="size-4 flex-none" />
}
];
@@ -44,20 +46,50 @@ export const orgNavSections = (
{
title: "sidebarSites",
href: "/{orgId}/settings/sites",
icon: <Combine className="h-4 w-4" />
icon: <Combine className="size-4 flex-none" />
},
{
title: "sidebarResources",
href: "/{orgId}/settings/resources",
icon: <Waypoints className="h-4 w-4" />
icon: <Waypoints className="size-4 flex-none" />,
items: [
{
title: "sidebarProxyResources",
href: "/{orgId}/settings/resources/proxy",
icon: <Globe className="size-4 flex-none" />
},
...(enableClients
? [
{
title: "sidebarClientResources",
href: "/{orgId}/settings/resources/client",
icon: (
<GlobeLock className="size-4 flex-none" />
)
}
]
: [])
]
},
...(enableClients
? [
{
title: "sidebarClients",
href: "/{orgId}/settings/clients",
icon: <MonitorUp className="h-4 w-4" />,
isBeta: true
icon: <MonitorUp className="size-4 flex-none" />,
isBeta: true,
items: [
{
href: "/{orgId}/settings/clients/user",
title: "sidebarUserDevices",
icon: (
<Smartphone className="size-4 flex-none" />
)
},
{
href: "/{orgId}/settings/clients/machine",
title: "sidebarMachineClients",
icon: <Server className="size-4 flex-none" />
}
]
}
]
: []),
@@ -66,7 +98,7 @@ export const orgNavSections = (
{
title: "sidebarRemoteExitNodes",
href: "/{orgId}/settings/remote-exit-nodes",
icon: <Server className="h-4 w-4" />,
icon: <Server className="size-4 flex-none" />,
showEE: true
}
]
@@ -74,12 +106,12 @@ export const orgNavSections = (
{
title: "sidebarDomains",
href: "/{orgId}/settings/domains",
icon: <Globe className="h-4 w-4" />
icon: <Globe className="size-4 flex-none" />
},
{
title: "sidebarBluePrints",
href: "/{orgId}/settings/blueprints",
icon: <ReceiptText className="h-4 w-4" />
icon: <ReceiptText className="size-4 flex-none" />
}
]
},
@@ -88,31 +120,31 @@ export const orgNavSections = (
items: [
{
title: "sidebarUsers",
icon: <User className="h-4 w-4" />,
icon: <User className="size-4 flex-none" />,
items: [
{
title: "sidebarUsers",
href: "/{orgId}/settings/access/users",
icon: <User className="h-4 w-4" />
icon: <User className="size-4 flex-none" />
},
{
title: "sidebarInvitations",
href: "/{orgId}/settings/access/invitations",
icon: <TicketCheck className="h-4 w-4" />
icon: <TicketCheck className="size-4 flex-none" />
}
]
},
{
title: "sidebarRoles",
href: "/{orgId}/settings/access/roles",
icon: <Users className="h-4 w-4" />
icon: <Users className="size-4 flex-none" />
},
...(build == "saas"
? [
{
title: "sidebarIdentityProviders",
href: "/{orgId}/settings/idp",
icon: <Fingerprint className="h-4 w-4" />,
icon: <Fingerprint className="size-4 flex-none" />,
showEE: true
}
]
@@ -120,7 +152,7 @@ export const orgNavSections = (
{
title: "sidebarShareableLinks",
href: "/{orgId}/settings/share-links",
icon: <LinkIcon className="h-4 w-4" />
icon: <LinkIcon className="size-4 flex-none" />
}
]
},
@@ -131,19 +163,19 @@ export const orgNavSections = (
{
title: "sidebarLogsRequest",
href: "/{orgId}/settings/logs/request",
icon: <SquareMousePointer className="h-4 w-4" />
icon: <SquareMousePointer className="size-4 flex-none" />
},
...(build != "oss"
? [
{
title: "sidebarLogsAccess",
href: "/{orgId}/settings/logs/access",
icon: <ScanEye className="h-4 w-4" />
icon: <ScanEye className="size-4 flex-none" />
},
{
title: "sidebarLogsAction",
href: "/{orgId}/settings/logs/action",
icon: <Logs className="h-4 w-4" />
icon: <Logs className="size-4 flex-none" />
}
]
: [])
@@ -158,7 +190,7 @@ export const orgNavSections = (
return [
{
title: "sidebarLogs",
icon: <Logs className="h-4 w-4" />,
icon: <Logs className="size-4 flex-none" />,
items: logItems
}
];
@@ -170,14 +202,14 @@ export const orgNavSections = (
{
title: "sidebarApiKeys",
href: "/{orgId}/settings/api-keys",
icon: <KeyRound className="h-4 w-4" />
icon: <KeyRound className="size-4 flex-none" />
},
...(build == "saas"
? [
{
title: "sidebarBilling",
href: "/{orgId}/settings/billing",
icon: <CreditCard className="h-4 w-4" />
icon: <CreditCard className="size-4 flex-none" />
}
]
: []),
@@ -186,14 +218,14 @@ export const orgNavSections = (
{
title: "sidebarEnterpriseLicenses",
href: "/{orgId}/settings/license",
icon: <TicketCheck className="h-4 w-4" />
icon: <TicketCheck className="size-4 flex-none" />
}
]
: []),
{
title: "sidebarSettings",
href: "/{orgId}/settings/general",
icon: <Settings className="h-4 w-4" />
icon: <Settings className="size-4 flex-none" />
}
]
}
@@ -206,24 +238,24 @@ export const adminNavSections: SidebarNavSection[] = [
{
title: "sidebarAllUsers",
href: "/admin/users",
icon: <Users className="h-4 w-4" />
icon: <Users className="size-4 flex-none" />
},
{
title: "sidebarApiKeys",
href: "/admin/api-keys",
icon: <KeyRound className="h-4 w-4" />
icon: <KeyRound className="size-4 flex-none" />
},
{
title: "sidebarIdentityProviders",
href: "/admin/idp",
icon: <Fingerprint className="h-4 w-4" />
icon: <Fingerprint className="size-4 flex-none" />
},
...(build == "enterprise"
? [
{
title: "sidebarLicense",
href: "/admin/license",
icon: <TicketCheck className="h-4 w-4" />
icon: <TicketCheck className="size-4 flex-none" />
}
]
: [])