From a7c212ffa4b71bc143c23760eb1ff50cf0c66d29 Mon Sep 17 00:00:00 2001
From: miloschwartz
Date: Tue, 21 Apr 2026 20:20:33 -0700
Subject: [PATCH 1/5] badge fixes
---
src/app/[orgId]/settings/access/users/create/page.tsx | 4 ++--
src/components/UsersTable.tsx | 1 +
src/components/alert-rule-editor/AlertRuleGraphEditor.tsx | 5 +++--
src/components/ui/badge.tsx | 4 ++--
4 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/src/app/[orgId]/settings/access/users/create/page.tsx b/src/app/[orgId]/settings/access/users/create/page.tsx
index 2c3292f9e..cec399a32 100644
--- a/src/app/[orgId]/settings/access/users/create/page.tsx
+++ b/src/app/[orgId]/settings/access/users/create/page.tsx
@@ -50,6 +50,7 @@ import IdpTypeIcon from "@app/components/IdpTypeIcon";
import { usePaidStatus } from "@app/hooks/usePaidStatus";
import { tierMatrix } from "@server/lib/billing/tierMatrix";
import OrgRolesTagField from "@app/components/OrgRolesTagField";
+import CopyToClipboard from "@app/components/CopyToClipboard";
type UserType = "internal" | "oidc";
@@ -670,9 +671,8 @@ export default function Page() {
days: expiresInDays
})}
-
diff --git a/src/components/UsersTable.tsx b/src/components/UsersTable.tsx
index 979c59425..50915c02b 100644
--- a/src/components/UsersTable.tsx
+++ b/src/components/UsersTable.tsx
@@ -382,6 +382,7 @@ export default function UsersTable({
pagination={pagination}
rowCount={rowCount}
isNavigatingToAddPage={isNavigatingToAddPage}
+ addButtonText={t("accessUserCreate")}
searchQuery={searchParams.get("query")?.toString()}
onSearch={handleSearchChange}
onPaginationChange={handlePaginationChange}
diff --git a/src/components/alert-rule-editor/AlertRuleGraphEditor.tsx b/src/components/alert-rule-editor/AlertRuleGraphEditor.tsx
index 524f17482..ef31c3476 100644
--- a/src/components/alert-rule-editor/AlertRuleGraphEditor.tsx
+++ b/src/components/alert-rule-editor/AlertRuleGraphEditor.tsx
@@ -39,6 +39,7 @@ import { useTranslations } from "next-intl";
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
import { SwitchInput } from "@app/components/SwitchInput";
import { tierMatrix } from "@server/lib/billing/tierMatrix";
+import { Badge } from "../ui/badge";
const FORM_ID = "alert-rule-form";
@@ -180,9 +181,9 @@ export default function AlertRuleGraphEditor({
>
{isNew && (
-
+
{t("alertingDraftBadge")}
-
+
)}
Date: Tue, 21 Apr 2026 20:29:05 -0700
Subject: [PATCH 2/5] add way to reject a pending site
---
messages/en-US.json | 3 +-
src/components/PendingSitesTable.tsx | 117 +++++++++++++++++++++------
2 files changed, 94 insertions(+), 26 deletions(-)
diff --git a/messages/en-US.json b/messages/en-US.json
index e4d55e4b9..89c6247db 100644
--- a/messages/en-US.json
+++ b/messages/en-US.json
@@ -3139,5 +3139,6 @@
"idpUnassociateMenu": "Unassociate",
"idpDeleteAllOrgsMenu": "Delete",
"publicIpEndpoint": "Endpoint",
- "lastTriggeredAt": "Last Trigger"
+ "lastTriggeredAt": "Last Trigger",
+ "reject": "Reject"
}
diff --git a/src/components/PendingSitesTable.tsx b/src/components/PendingSitesTable.tsx
index a6625037d..d32aee95e 100644
--- a/src/components/PendingSitesTable.tsx
+++ b/src/components/PendingSitesTable.tsx
@@ -1,5 +1,6 @@
"use client";
+import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
import { Badge } from "@app/components/ui/badge";
import { Button } from "@app/components/ui/button";
import {
@@ -24,7 +25,8 @@ import {
ArrowUpRight,
Check,
ChevronsUpDownIcon,
- MoreHorizontal
+ MoreHorizontal,
+ X
} from "lucide-react";
import { useTranslations } from "next-intl";
import Link from "next/link";
@@ -62,6 +64,9 @@ export default function PendingSitesTable({
const [isRefreshing, startTransition] = useTransition();
const [approvingIds, setApprovingIds] = useState>(new Set());
+ const [rejectingIds, setRejectingIds] = useState>(new Set());
+ const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
+ const [selectedSite, setSelectedSite] = useState(null);
const api = createApiClient(useEnvContext());
const t = useTranslations();
@@ -128,6 +133,33 @@ export default function PendingSitesTable({
}
}
+ async function rejectSite(siteId: number) {
+ setRejectingIds((prev) => new Set(prev).add(siteId));
+ try {
+ await api.delete(`/site/${siteId}`);
+ toast({
+ title: t("success"),
+ description: t("siteDeleted"),
+ variant: "default"
+ });
+ setIsDeleteModalOpen(false);
+ setSelectedSite(null);
+ router.refresh();
+ } catch (e) {
+ toast({
+ variant: "destructive",
+ title: t("siteErrorDelete"),
+ description: formatAxiosError(e, t("siteErrorDelete"))
+ });
+ } finally {
+ setRejectingIds((prev) => {
+ const next = new Set(prev);
+ next.delete(siteId);
+ return next;
+ });
+ }
+ }
+
const columns: ExtendedColumnDef[] = [
{
accessorKey: "name",
@@ -387,6 +419,7 @@ export default function PendingSitesTable({
cell: ({ row }) => {
const siteRow = row.original;
const isApproving = approvingIds.has(siteRow.id);
+ const isRejecting = rejectingIds.has(siteRow.id);
return (
@@ -409,7 +442,18 @@ export default function PendingSitesTable({
+
@@ -606,14 +603,18 @@ export function ControlledDataTable({
))
) : (
-
-
- No results found.
-
-
+
+ {addAction}
+
+ )
+ : undefined
+ }
+ />
)}
diff --git a/src/components/ui/data-table-empty-state.tsx b/src/components/ui/data-table-empty-state.tsx
new file mode 100644
index 000000000..793c360f4
--- /dev/null
+++ b/src/components/ui/data-table-empty-state.tsx
@@ -0,0 +1,46 @@
+"use client";
+
+import { TableCell, TableRow } from "@/components/ui/table";
+import { useTranslations } from "next-intl";
+import { type ReactNode } from "react";
+
+const PLACEHOLDER_ROW_COUNT = 5;
+
+type DataTableEmptyStateProps = {
+ colSpan: number;
+ action?: ReactNode;
+};
+
+export function DataTableEmptyState({
+ colSpan,
+ action
+}: DataTableEmptyStateProps) {
+ const t = useTranslations();
+ return (
+
+
+
+
+ {Array.from({ length: PLACEHOLDER_ROW_COUNT }).map(
+ (_, i) => (
+
+ )
+ )}
+
+
+
+ {t("noResults")}
+
+ {action}
+
+
+
+
+ );
+}
diff --git a/src/components/ui/data-table.tsx b/src/components/ui/data-table.tsx
index 2c0e5e48c..82aafe1f4 100644
--- a/src/components/ui/data-table.tsx
+++ b/src/components/ui/data-table.tsx
@@ -29,6 +29,7 @@ import {
TableHeader,
TableRow
} from "@/components/ui/table";
+import { DataTableEmptyState } from "@/components/ui/data-table-empty-state";
import { Button } from "@app/components/ui/button";
import { useEffect, useMemo, useRef, useState } from "react";
import { Input } from "@app/components/ui/input";
@@ -515,6 +516,36 @@ export function DataTable({
return "";
};
+ const tableRows = table.getRowModel().rows;
+ const hasRows = tableRows.length > 0;
+ const hasAddAction = Boolean(
+ addButtonText && ((addActions && addActions.length > 0) || onAdd)
+ );
+ const showAddActionInEmptyState = !hasRows && hasAddAction;
+ const addAction = addActions && addActions.length > 0 && addButtonText ? (
+
+
+
+
+ {addButtonText}
+
+
+
+
+ {addActions.map((action, i) => (
+ action.onSelect()}>
+ {action.label}
+
+ ))}
+
+
+ ) : onAdd && addButtonText ? (
+
+
+ {addButtonText}
+
+ ) : null;
+
return (
@@ -651,45 +682,15 @@ export function DataTable({
)}
- {addActions && addActions.length > 0 && addButtonText ? (
-
-
-
-
-
- {addButtonText}
-
-
-
-
- {addActions.map((action, i) => (
-
- action.onSelect()
- }
- >
- {action.label}
-
- ))}
-
-
-
- ) : (
- onAdd &&
- addButtonText && (
-
-
-
- {addButtonText}
-
-
- )
+ {addAction && (
+ <>
+ {addAction}
+ {!showAddActionInEmptyState && (
+
+ {addAction}
+
+ )}
+ >
)}
@@ -884,14 +885,18 @@ export function DataTable({
))
) : (
-
-
- No results found.
-
-
+
+ {addAction}
+
+ )
+ : undefined
+ }
+ />
)}
From 7f5c164e1606a4b9a7bef24d4b597056c1fe674a Mon Sep 17 00:00:00 2001
From: miloschwartz
Date: Tue, 21 Apr 2026 20:51:59 -0700
Subject: [PATCH 4/5] change logging
---
server/apiServer.ts | 2 +-
server/internalServer.ts | 2 +-
server/lib/telemetry.ts | 2 +-
server/nextServer.ts | 2 +-
server/private/lib/acmeCertSync.ts | 10 +++++-----
server/routers/newt/pingAccumulator.ts | 2 +-
6 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/server/apiServer.ts b/server/apiServer.ts
index dafec1b85..9a91d473e 100644
--- a/server/apiServer.ts
+++ b/server/apiServer.ts
@@ -121,7 +121,7 @@ export function createApiServer() {
const httpServer = apiServer.listen(externalPort, (err?: any) => {
if (err) throw err;
logger.info(
- `API server is running on http://localhost:${externalPort}`
+ `Dashboard API server is running on http://localhost:${externalPort}`
);
});
diff --git a/server/internalServer.ts b/server/internalServer.ts
index 7ba046e4b..83872e7f9 100644
--- a/server/internalServer.ts
+++ b/server/internalServer.ts
@@ -36,7 +36,7 @@ export function createInternalServer() {
internalServer.listen(internalPort, (err?: any) => {
if (err) throw err;
logger.info(
- `Internal server is running on http://localhost:${internalPort}`
+ `Internal API server is running on http://localhost:${internalPort}`
);
});
diff --git a/server/lib/telemetry.ts b/server/lib/telemetry.ts
index fda59f394..8d341bf1d 100644
--- a/server/lib/telemetry.ts
+++ b/server/lib/telemetry.ts
@@ -72,7 +72,7 @@ class TelemetryClient {
logger.debug("Successfully sent analytics data");
});
},
- 48 * 60 * 60 * 1000
+ 336 * 60 * 60 * 1000
);
this.collectAndSendAnalytics().catch((err) => {
diff --git a/server/nextServer.ts b/server/nextServer.ts
index b862a699c..e5e81b724 100644
--- a/server/nextServer.ts
+++ b/server/nextServer.ts
@@ -29,7 +29,7 @@ export async function createNextServer() {
nextServer.listen(nextPort, (err?: any) => {
if (err) throw err;
logger.info(
- `Next.js server is running on http://localhost:${nextPort}`
+ `Dashboard Web UI server is running on http://localhost:${nextPort}`
);
});
diff --git a/server/private/lib/acmeCertSync.ts b/server/private/lib/acmeCertSync.ts
index faa45b08e..62a18b805 100644
--- a/server/private/lib/acmeCertSync.ts
+++ b/server/private/lib/acmeCertSync.ts
@@ -89,7 +89,7 @@ async function pushCertUpdateToAffectedNewts(
return;
}
- logger.info(
+ logger.debug(
`acmeCertSync: pushing cert update to ${affectedResources.length} affected site resource(s) for domain "${domain}"`
);
@@ -187,7 +187,7 @@ async function pushCertUpdateToAffectedNewts(
newt.version
);
- logger.info(
+ logger.debug(
`acmeCertSync: pushed cert update to newt for site ${siteId}, resource ${resource.siteResourceId}`
);
}
@@ -400,7 +400,7 @@ async function syncAcmeCerts(
})
.where(eq(certificates.domain, domain));
- logger.info(
+ logger.debug(
`acmeCertSync: updated certificate for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})`
);
@@ -423,7 +423,7 @@ async function syncAcmeCerts(
wildcard
});
- logger.info(
+ logger.debug(
`acmeCertSync: inserted new certificate for ${domain} (expires ${expiresAt ? new Date(expiresAt * 1000).toISOString() : "unknown"})`
);
@@ -461,7 +461,7 @@ export function initAcmeCertSync(): void {
const resolver = privateConfigData.acme?.resolver ?? "letsencrypt";
const intervalMs = privateConfigData.acme?.sync_interval_ms ?? 5000;
- logger.info(
+ logger.debug(
`acmeCertSync: starting ACME cert sync from "${acmeJsonPath}" using resolver "${resolver}" every ${intervalMs}ms`
);
diff --git a/server/routers/newt/pingAccumulator.ts b/server/routers/newt/pingAccumulator.ts
index b63bf97d3..9b2f04c8e 100644
--- a/server/routers/newt/pingAccumulator.ts
+++ b/server/routers/newt/pingAccumulator.ts
@@ -381,7 +381,7 @@ export function startPingAccumulator(): void {
// Don't prevent the process from exiting
flushTimer.unref();
- logger.info(
+ logger.debug(
`Ping accumulator started (flush interval: ${FLUSH_INTERVAL_MS}ms)`
);
}
From 19e0452d844c36b28559536c29d3edf521400326 Mon Sep 17 00:00:00 2001
From: miloschwartz
Date: Tue, 21 Apr 2026 20:54:57 -0700
Subject: [PATCH 5/5] remove arrow icon on server admin
---
src/components/LayoutMobileMenu.tsx | 1 -
src/components/LayoutSidebar.tsx | 1 -
2 files changed, 2 deletions(-)
diff --git a/src/components/LayoutMobileMenu.tsx b/src/components/LayoutMobileMenu.tsx
index 854cad6db..13efdd564 100644
--- a/src/components/LayoutMobileMenu.tsx
+++ b/src/components/LayoutMobileMenu.tsx
@@ -101,7 +101,6 @@ export function LayoutMobileMenu({
"serverAdmin"
)}
-
)}
diff --git a/src/components/LayoutSidebar.tsx b/src/components/LayoutSidebar.tsx
index a66a8300b..19a095419 100644
--- a/src/components/LayoutSidebar.tsx
+++ b/src/components/LayoutSidebar.tsx
@@ -189,7 +189,6 @@ export function LayoutSidebar({
{t("serverAdmin")}
-
>
)}