diff --git a/npm-packages/common/config/rush/pnpm-lock.yaml b/npm-packages/common/config/rush/pnpm-lock.yaml
index 3ab2af62..187d7f80 100644
--- a/npm-packages/common/config/rush/pnpm-lock.yaml
+++ b/npm-packages/common/config/rush/pnpm-lock.yaml
@@ -612,9 +612,6 @@ importers:
formik:
specifier: ~2.2.9
version: 2.2.10(react@18.3.1)
- js-base64:
- specifier: ~3.7.4
- version: 3.7.7
launchdarkly-react-client-sdk:
specifier: ^3.0.4
version: 3.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -633,9 +630,6 @@ importers:
posthog-js:
specifier: ~1.211.3
version: 1.211.3
- prop-types:
- specifier: ~15.8.1
- version: 15.8.1
react:
specifier: ^18.0.0
version: 18.3.1
@@ -651,9 +645,6 @@ importers:
react-sparklines:
specifier: ~1.7.0
version: 1.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- react-table:
- specifier: 7.8.0
- version: 7.8.0(react@18.3.1)
react-use:
specifier: ~17.6.0
version: 17.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -901,9 +892,6 @@ importers:
'@radix-ui/react-tooltip':
specifier: ~1.1.0
version: 1.1.7(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@sentry/nextjs':
- specifier: 7.120.2
- version: 7.120.2(encoding@0.1.13)(next@14.2.21(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(supports-color@10.0.0)(webpack@5.97.1(@swc/core@1.10.4(@swc/helpers@0.5.15)))
'@tanstack/react-table':
specifier: ~8.20.6
version: 8.20.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -26929,7 +26917,7 @@ snapshots:
'@sentry/types': 7.120.2
'@sentry/utils': 7.120.2
- '@sentry/cli@1.77.3(encoding@0.1.13)(supports-color@10.0.0)':
+ '@sentry/cli@1.77.3(encoding@0.1.13)':
dependencies:
https-proxy-agent: 5.0.1(supports-color@10.0.0)
mkdirp: 0.5.6
@@ -26968,7 +26956,7 @@ snapshots:
'@sentry/types': 7.120.2
'@sentry/utils': 7.120.2
'@sentry/vercel-edge': 7.120.2
- '@sentry/webpack-plugin': 1.21.0(encoding@0.1.13)(supports-color@10.0.0)
+ '@sentry/webpack-plugin': 1.21.0(encoding@0.1.13)
chalk: 3.0.0
next: 14.2.21(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
@@ -26981,29 +26969,6 @@ snapshots:
- encoding
- supports-color
- '@sentry/nextjs@7.120.2(encoding@0.1.13)(next@14.2.21(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(supports-color@10.0.0)(webpack@5.97.1(@swc/core@1.10.4(@swc/helpers@0.5.15)))':
- dependencies:
- '@rollup/plugin-commonjs': 24.0.0(rollup@2.79.2)
- '@sentry/core': 7.120.2
- '@sentry/integrations': 7.120.2
- '@sentry/node': 7.120.2
- '@sentry/react': 7.120.2(react@18.3.1)
- '@sentry/types': 7.120.2
- '@sentry/utils': 7.120.2
- '@sentry/vercel-edge': 7.120.2
- '@sentry/webpack-plugin': 1.21.0(encoding@0.1.13)(supports-color@10.0.0)
- chalk: 3.0.0
- next: 14.2.21(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- react: 18.3.1
- resolve: 1.22.8
- rollup: 2.79.2
- stacktrace-parser: 0.1.10
- optionalDependencies:
- webpack: 5.97.1(@swc/core@1.10.4(@swc/helpers@0.5.15))
- transitivePeerDependencies:
- - encoding
- - supports-color
-
'@sentry/node@7.120.2':
dependencies:
'@sentry-internal/tracing': 7.120.2
@@ -27052,9 +27017,9 @@ snapshots:
'@sentry/types': 7.120.2
'@sentry/utils': 7.120.2
- '@sentry/webpack-plugin@1.21.0(encoding@0.1.13)(supports-color@10.0.0)':
+ '@sentry/webpack-plugin@1.21.0(encoding@0.1.13)':
dependencies:
- '@sentry/cli': 1.77.3(encoding@0.1.13)(supports-color@10.0.0)
+ '@sentry/cli': 1.77.3(encoding@0.1.13)
webpack-sources: 3.2.3
transitivePeerDependencies:
- encoding
diff --git a/npm-packages/dashboard-common/package.json b/npm-packages/dashboard-common/package.json
index 890e051b..b53275a7 100644
--- a/npm-packages/dashboard-common/package.json
+++ b/npm-packages/dashboard-common/package.json
@@ -23,7 +23,6 @@
"@monaco-editor/react": "4.5.1",
"@radix-ui/react-icons": "~1.3.0",
"@radix-ui/react-tooltip": "~1.1.0",
- "@sentry/nextjs": "7.120.2",
"@tanstack/react-table": "~8.20.6",
"acorn": "~8.8.1",
"base64-js": "^1.5.1",
diff --git a/npm-packages/dashboard-common/src/elements/BigChart.tsx b/npm-packages/dashboard-common/src/elements/BigChart.tsx
index 78eb7c3c..b6bfb3c5 100644
--- a/npm-packages/dashboard-common/src/elements/BigChart.tsx
+++ b/npm-packages/dashboard-common/src/elements/BigChart.tsx
@@ -1,5 +1,4 @@
-import { captureException } from "@sentry/nextjs";
-import { useEffect, useState } from "react";
+import { useContext, useEffect, useState } from "react";
import {
CartesianGrid,
Legend,
@@ -14,6 +13,7 @@ import { LoadingTransition } from "@common/elements/Loading";
import { ChartData, ChartDataSource } from "@common/lib/charts/types";
import { Callout } from "@common/elements/Callout";
import { ChartTooltip } from "@common/elements/ChartTooltip";
+import { DeploymentInfoContext } from "@common/lib/deploymentContext";
export function BigChart({
dataSources,
@@ -33,6 +33,8 @@ export function BigChart({
const [startDate] = useState(initStartDate);
const [endDate] = useState(initEndDate);
+ const { captureException } = useContext(DeploymentInfoContext);
+
useEffect(() => {
async function getChartData() {
if (startDate < endDate) {
@@ -55,7 +57,7 @@ export function BigChart({
}
}
void getChartData();
- }, [dataSources, startDate, endDate]);
+ }, [dataSources, startDate, endDate, captureException]);
return (
diff --git a/npm-packages/dashboard-common/src/elements/ObjectEditor/ObjectEditor.tsx b/npm-packages/dashboard-common/src/elements/ObjectEditor/ObjectEditor.tsx
index b6fd76de..b20337c0 100644
--- a/npm-packages/dashboard-common/src/elements/ObjectEditor/ObjectEditor.tsx
+++ b/npm-packages/dashboard-common/src/elements/ObjectEditor/ObjectEditor.tsx
@@ -11,7 +11,6 @@ import React, { useCallback, useEffect, useRef, useState } from "react";
import isArray from "lodash/isArray";
import isPlainObject from "lodash/isPlainObject";
import { UNDEFINED_PLACEHOLDER } from "system-udfs/convex/_system/frontend/patchDocumentsFields";
-import { captureMessage } from "@sentry/nextjs";
import { useMount } from "react-use";
import { useRouter } from "next/router";
import { stringifyValue } from "@common/lib/stringifyValue";
@@ -333,7 +332,6 @@ function setErrorMarkers(
.getModels()
?.find((m) => path.replace(":", "_") === m.uri.path.slice(1));
if (!model) {
- captureMessage(`Model not found in Monaco editor for path: ${path}`);
return;
}
monaco.editor.setModelMarkers(model, "", markers);
diff --git a/npm-packages/dashboard-common/src/elements/ObjectEditor/useIdDecorations.tsx b/npm-packages/dashboard-common/src/elements/ObjectEditor/useIdDecorations.tsx
index a764c187..61a4db9d 100644
--- a/npm-packages/dashboard-common/src/elements/ObjectEditor/useIdDecorations.tsx
+++ b/npm-packages/dashboard-common/src/elements/ObjectEditor/useIdDecorations.tsx
@@ -7,7 +7,6 @@ import { useRouter } from "next/router";
import { cn } from "@common/lib/cn";
import { GenericDocument } from "convex/server";
import { SourceLocation } from "acorn";
-import { captureMessage } from "@sentry/nextjs";
import type { editor } from "monaco-editor/esm/vs/editor/editor.api";
import { stringifyValue } from "@common/lib/stringifyValue";
import {
@@ -69,7 +68,6 @@ export function useIdDecorations(
.getModels()
?.find((m) => path.replace(":", "_") === m.uri.path.slice(1));
if (!model) {
- captureMessage(`Model not found in Monaco editor for path: ${path}`);
return;
}
diff --git a/npm-packages/dashboard-common/src/features/data/components/DataToolbar/ClearTableConfirmation.tsx b/npm-packages/dashboard-common/src/features/data/components/DataToolbar/ClearTableConfirmation.tsx
index 244bf57f..dffccb9a 100644
--- a/npm-packages/dashboard-common/src/features/data/components/DataToolbar/ClearTableConfirmation.tsx
+++ b/npm-packages/dashboard-common/src/features/data/components/DataToolbar/ClearTableConfirmation.tsx
@@ -1,10 +1,10 @@
-import { captureException } from "@sentry/nextjs";
import { Cursor } from "convex/server";
-import { useState } from "react";
+import { useContext, useState } from "react";
import { useMountedState } from "react-use";
import { useInvalidateShapes } from "@common/features/data/lib/api";
import { ConfirmationDialog } from "@common/elements/ConfirmationDialog";
import { toast } from "@common/lib/utils";
+import { DeploymentInfoContext } from "@common/lib/deploymentContext";
export function ClearTableConfirmation({
numRows,
@@ -49,6 +49,8 @@ export function ClearTableConfirmation({
const invalidateShapes = useInvalidateShapes();
+ const { captureException } = useContext(DeploymentInfoContext);
+
return (
({
- id: 0,
- name: "Team",
- slug: "team",
- }),
- useTeamMembers: () => [],
- useTeamEntitlements: () => ({
- auditLogsEnabled: true,
- }),
- useCurrentUsageBanner: () => null,
- useCurrentProject: () => ({
- id: 0,
- name: "Project",
- slug: "project",
- teamId: 0,
- }),
- useLogDeploymentEvent: () => () => {},
- useCurrentDeployment: () => ({
- id: 0,
- name: "local",
- deploymentType: "prod",
- projectId: 0,
- kind: "local",
- previewIdentifier: null,
- }),
- useHasProjectAdminPermissions: () => true,
- useIsDeploymentPaused: () => false,
- useProjectEnvironmentVariables: () => ({ configs: [] }),
- CloudImport: ({ sourceCloudBackupId }: { sourceCloudBackupId: number }) => (
- {sourceCloudBackupId}
- ),
- TeamMemberLink: () => ,
- useTeamUsageState: () => "Default",
- teamsURI: "/",
- projectsURI: "/",
- deploymentsURI: "/",
- isSelfHosted: true,
-};
+import { mockDeploymentInfo } from "@common/lib/mockDeploymentInfo";
jest.mock("convex/react", () => ({
useQuery: jest.fn(),
@@ -121,7 +76,9 @@ describe("DataToolbar", () => {
// @ts-expect-error
useRouter.mockReturnValue({ query, replace: jest.fn() });
return render(
- ,
+
+ ,
+ ,
);
};
@@ -156,35 +113,34 @@ describe("DataToolbar", () => {
activeSchema: null,
...hookProps,
});
+
return (
-
- new Map(), [])}>
- {popupState.popupEl}
-
-
-
+ new Map(), [])}>
+ {popupState.popupEl}
+
+
);
}
diff --git a/npm-packages/dashboard-common/src/features/data/components/Table/TableContextMenu.test.tsx b/npm-packages/dashboard-common/src/features/data/components/Table/TableContextMenu.test.tsx
index a5f6d158..b127b851 100644
--- a/npm-packages/dashboard-common/src/features/data/components/Table/TableContextMenu.test.tsx
+++ b/npm-packages/dashboard-common/src/features/data/components/Table/TableContextMenu.test.tsx
@@ -9,6 +9,8 @@ import {
TableContextMenuProps,
} from "@common/features/data/components/Table/TableContextMenu";
import { mockConvexReactClient } from "@common/lib/mockConvexReactClient";
+import { DeploymentInfoContext } from "@common/lib/deploymentContext";
+import { mockDeploymentInfo } from "@common/lib/mockDeploymentInfo";
jest.mock("next/router", () => jest.requireActual("next-router-mock"));
@@ -72,9 +74,11 @@ describe("TableContextMenu", () => {
const renderWithProvider = (props: Partial = {}) =>
render(
-
-
- ,
+
+
+
+
+ ,
);
beforeEach(() => {
diff --git a/npm-packages/dashboard-common/src/features/data/components/Table/TableContextMenu.tsx b/npm-packages/dashboard-common/src/features/data/components/Table/TableContextMenu.tsx
index 911490bb..8be5c6c1 100644
--- a/npm-packages/dashboard-common/src/features/data/components/Table/TableContextMenu.tsx
+++ b/npm-packages/dashboard-common/src/features/data/components/Table/TableContextMenu.tsx
@@ -1,4 +1,3 @@
-import * as Sentry from "@sentry/nextjs";
import {
ClipboardCopyIcon,
EnterFullScreenIcon,
@@ -8,7 +7,7 @@ import {
TrashIcon,
} from "@radix-ui/react-icons";
import FunnelIcon from "@heroicons/react/24/outline/FunnelIcon";
-import React, { useCallback, useState } from "react";
+import React, { useCallback, useContext, useState } from "react";
import { GenericDocument } from "convex/server";
import { Value, convexToJson } from "convex/values";
import {
@@ -28,6 +27,7 @@ import {
} from "@common/features/data/components/Table/DataCell/utils/cellActions";
import { stringifyValue } from "@common/lib/stringifyValue";
import { useNents } from "@common/lib/useNents";
+import { DeploymentInfoContext } from "@common/lib/deploymentContext";
export function useTableContextMenuState(): {
contextMenuState: TableContextMenuState | null;
@@ -88,6 +88,8 @@ export function TableContextMenu({
selectedNent && selectedNent.state !== "active"
);
+ const { captureMessage } = useContext(DeploymentInfoContext);
+
const disableEditDoc = !canManageTable || isInUnmountedComponent;
const disableEdit =
state?.selectedCell?.column.startsWith("_") || disableEditDoc;
@@ -113,9 +115,7 @@ export function TableContextMenu({
const selectedRowId = state.selectedCell?.rowId;
const document = data.find((row) => row._id === selectedRowId);
if (!document) {
- Sentry.captureMessage(
- "Can’t find the right-clicked document in data",
- );
+ captureMessage("Can’t find the right-clicked document in data");
return;
}
}
@@ -383,8 +383,9 @@ function FilterWithSubmenu({
addDraftFilter: (newFilter: Filter) => void;
defaultDocument: GenericDocument;
}) {
+ const { captureMessage } = useContext(DeploymentInfoContext);
if (!state.selectedCell) {
- Sentry.captureMessage("No selected cell in FilterWithSubmenu");
+ captureMessage("No selected cell in FilterWithSubmenu");
return null;
}
return (
diff --git a/npm-packages/dashboard-common/src/features/data/components/Table/utils/arrowKeyHandler.ts b/npm-packages/dashboard-common/src/features/data/components/Table/utils/arrowKeyHandler.ts
index fcc364b1..6369d575 100644
--- a/npm-packages/dashboard-common/src/features/data/components/Table/utils/arrowKeyHandler.ts
+++ b/npm-packages/dashboard-common/src/features/data/components/Table/utils/arrowKeyHandler.ts
@@ -1,5 +1,3 @@
-import { captureMessage } from "@sentry/nextjs";
-
function newDirection(
{ colIndex, rowIndex }: { colIndex: number; rowIndex: number },
key: string,
@@ -14,7 +12,6 @@ function newDirection(
case "ArrowDown":
return { newColIndex: colIndex, newRowIndex: rowIndex + 1 };
default:
- captureMessage(`Unexpected key ${key}`);
return { newColIndex: colIndex, newRowIndex: rowIndex };
}
}
diff --git a/npm-packages/dashboard-common/src/features/data/lib/api.ts b/npm-packages/dashboard-common/src/features/data/lib/api.ts
index 328c1d63..6aeee522 100644
--- a/npm-packages/dashboard-common/src/features/data/lib/api.ts
+++ b/npm-packages/dashboard-common/src/features/data/lib/api.ts
@@ -1,6 +1,5 @@
-import { useCallback } from "react";
+import { useCallback, useContext } from "react";
import useSWR, { useSWRConfig } from "swr";
-import { reportHttpError } from "@common/lib/utils";
import {
useDeploymentUrl,
useAdminKey,
@@ -10,6 +9,7 @@ import {
} from "@common/lib/deploymentApi";
import { deploymentFetch } from "@common/lib/fetching";
import { useNents } from "@common/lib/useNents";
+import { DeploymentInfoContext } from "@common/lib/deploymentContext";
export function useDeleteTables(): (
tableNames: string[],
@@ -17,6 +17,7 @@ export function useDeleteTables(): (
) => Promise<{ success: false; error: string } | { success: true }> {
const deploymentUrl = useDeploymentUrl();
const adminKey = useAdminKey();
+ const { reportHttpError } = useContext(DeploymentInfoContext);
return async (tableNames: string[], componentId: string | null) => {
const body = JSON.stringify({ tableNames, componentId });
diff --git a/npm-packages/dashboard-common/src/features/data/lib/useDataToolbarActions.ts b/npm-packages/dashboard-common/src/features/data/lib/useDataToolbarActions.ts
index 4a67923c..c18ccfce 100644
--- a/npm-packages/dashboard-common/src/features/data/lib/useDataToolbarActions.ts
+++ b/npm-packages/dashboard-common/src/features/data/lib/useDataToolbarActions.ts
@@ -1,6 +1,5 @@
import { Cursor, GenericDocument } from "convex/server";
import { ConvexError } from "convex/values";
-import { captureException } from "@sentry/nextjs";
import { useMutation } from "convex/react";
import udfs from "udfs";
import { Id } from "system-udfs/convex/_generated/dataModel";
@@ -10,6 +9,8 @@ import {
} from "@common/features/data/lib/api";
import { useNents } from "@common/lib/useNents";
import { toast } from "@common/lib/utils";
+import { useContext } from "react";
+import { DeploymentInfoContext } from "@common/lib/deploymentContext";
export function useDataToolbarActions({
tableName,
@@ -33,6 +34,7 @@ export function useDataToolbarActions({
) => Promise<{ continueCursor: Cursor; deleted: number; hasMore: boolean }>;
deleteRows: (rowIds: Set) => Promise;
} {
+ const { captureException } = useContext(DeploymentInfoContext);
const invalidateShapes = useInvalidateShapes();
const documentAdd = useMutation(udfs.addDocument.default);
diff --git a/npm-packages/dashboard-common/src/features/data/lib/useToolPopup.tsx b/npm-packages/dashboard-common/src/features/data/lib/useToolPopup.tsx
index 1117862e..9d7befca 100644
--- a/npm-packages/dashboard-common/src/features/data/lib/useToolPopup.tsx
+++ b/npm-packages/dashboard-common/src/features/data/lib/useToolPopup.tsx
@@ -1,8 +1,7 @@
-import { captureMessage } from "@sentry/nextjs";
import { useMutation } from "convex/react";
import { Cursor, GenericDocument } from "convex/server";
import { ConvexError, ValidatorJSON } from "convex/values";
-import { useState } from "react";
+import { useContext, useState } from "react";
import udfs from "udfs";
import { SchemaJson } from "system-udfs/convex/_system/frontend/lib/filters";
import { useNents } from "@common/lib/useNents";
@@ -15,6 +14,7 @@ import { EditFieldsPanel } from "@common/features/data/components/Table/EditDocu
import { TableMetrics } from "@common/features/data/components/TableMetrics";
import { TableSchemaAndIndexes } from "@common/features/data/components/TableSchemaAndIndexes";
import { useDefaultDocument } from "@common/features/data/lib/useDefaultDocument";
+import { DeploymentInfoContext } from "@common/lib/deploymentContext";
export type PopupState = ReturnType;
@@ -197,6 +197,7 @@ function EditSingleDocumentPanel({
const replaceDocument = useMutation(udfs.replaceDocument.default);
const invalidateShapes = useInvalidateShapes();
const { selectedNent } = useNents();
+ const { captureMessage } = useContext(DeploymentInfoContext);
return (
({
- id: 0,
- name: "Team",
- slug: "team",
- }),
- useTeamMembers: () => [],
- useTeamEntitlements: () => ({
- auditLogsEnabled: true,
- }),
- useCurrentUsageBanner: () => null,
- useCurrentProject: () => ({
- id: 0,
- name: "Project",
- slug: "project",
- teamId: 0,
- }),
- useLogDeploymentEvent: () => () => {},
- useCurrentDeployment: () => ({
- id: 0,
- name: "local",
- deploymentType: "prod",
- projectId: 0,
- kind: "local",
- previewIdentifier: null,
- }),
- useHasProjectAdminPermissions: () => true,
- useIsDeploymentPaused: () => false,
- useProjectEnvironmentVariables: () => ({ configs: [] }),
- CloudImport: ({ sourceCloudBackupId }: { sourceCloudBackupId: number }) => (
- {sourceCloudBackupId}
- ),
- TeamMemberLink: () => ,
- useTeamUsageState: () => "Default",
- teamsURI: "/",
- projectsURI: "/",
- deploymentsURI: "/",
- isSelfHosted: true,
-};
+import { DeploymentInfoContext } from "@common/lib/deploymentContext";
+import { mockDeploymentInfo } from "@common/lib/mockDeploymentInfo";
const mockRouter = jest
.fn()
@@ -100,7 +55,7 @@ describe("FileStorageContent", () => {
const setup = () =>
act(() =>
render(
-
+
@@ -140,7 +95,7 @@ describe("FileStorageContent", () => {
const setup = () =>
act(() =>
render(
-
+
diff --git a/npm-packages/dashboard-common/src/features/files/components/FileStorageView.tsx b/npm-packages/dashboard-common/src/features/files/components/FileStorageView.tsx
index d158d2da..ee463bfc 100644
--- a/npm-packages/dashboard-common/src/features/files/components/FileStorageView.tsx
+++ b/npm-packages/dashboard-common/src/features/files/components/FileStorageView.tsx
@@ -6,7 +6,6 @@ import {
TrashIcon,
UploadIcon,
} from "@radix-ui/react-icons";
-import * as Sentry from "@sentry/nextjs";
import { useMutation, usePaginatedQuery, useQuery } from "convex/react";
import Link from "next/link";
import React, { useContext, useEffect, useMemo, useRef, useState } from "react";
@@ -240,7 +239,6 @@ export function useUploadFiles() {
}
return { status: "success", name: file.name };
} catch (err) {
- Sentry.captureException(err);
return { status: "failure", name: file.name };
}
}
diff --git a/npm-packages/dashboard-common/src/features/schedules/lib/CronsProvider.tsx b/npm-packages/dashboard-common/src/features/schedules/lib/CronsProvider.tsx
index 9bf3220a..5bb7318e 100644
--- a/npm-packages/dashboard-common/src/features/schedules/lib/CronsProvider.tsx
+++ b/npm-packages/dashboard-common/src/features/schedules/lib/CronsProvider.tsx
@@ -1,5 +1,4 @@
-import * as Sentry from "@sentry/nextjs";
-import { ReactNode, useMemo } from "react";
+import { ReactNode, useContext, useMemo } from "react";
import { useQuery } from "convex/react";
import udfs from "udfs";
import {
@@ -12,6 +11,7 @@ import { useInMemoryDocumentCache } from "@common/features/schedules/lib/useInMe
import { useListModules } from "@common/lib/functions/useListModules";
import { createContextHook } from "@common/lib/createContextHook";
import { useNents } from "@common/lib/useNents";
+import { DeploymentInfoContext } from "@common/lib/deploymentContext";
type CronJobsContextType = {
cronsModule: Module | undefined;
@@ -30,6 +30,7 @@ export function CronJobsProviderWithCronHistory({
}: {
children: ReactNode;
}) {
+ const { captureMessage } = useContext(DeploymentInfoContext);
// Get functions
const modules = useListModules();
// Get cron jobs
@@ -82,16 +83,14 @@ export function CronJobsProviderWithCronHistory({
.map((identifier) => {
const cronJob = cronJobsMap.get(identifier)!;
if (!cronJob) {
- Sentry.captureMessage(
- `No CronJob found for CronSpec ${identifier}`,
- );
+ captureMessage(`No CronJob found for CronSpec ${identifier}`);
}
return cronJob;
})
.filter((x) => x), // remove empty
cronsModuleInner,
];
- }, [modules, cronJobs, cronJobRuns]);
+ }, [cronJobs, modules, cronJobRuns, captureMessage]);
return (
Promise {
const deploymentUrl = useDeploymentUrl();
const adminKey = useAdminKey();
const { selectedNent } = useNents();
- const { deploymentsURI } = useContext(DeploymentInfoContext);
+ const { deploymentsURI, reportHttpError } = useContext(DeploymentInfoContext);
return async (udfPath?: string) => {
const body = JSON.stringify({
@@ -65,6 +65,7 @@ export function useCancelJob(): (
) => Promise {
const deploymentUrl = useDeploymentUrl();
const adminKey = useAdminKey();
+ const { reportHttpError } = useContext(DeploymentInfoContext);
return async (id: string, componentId: string | null) => {
const body = JSON.stringify({ id, componentId });
diff --git a/npm-packages/dashboard-common/src/features/settings/components/DeploymentEnvironmentVariables.test.tsx b/npm-packages/dashboard-common/src/features/settings/components/DeploymentEnvironmentVariables.test.tsx
index cbc2b281..1991f7b7 100644
--- a/npm-packages/dashboard-common/src/features/settings/components/DeploymentEnvironmentVariables.test.tsx
+++ b/npm-packages/dashboard-common/src/features/settings/components/DeploymentEnvironmentVariables.test.tsx
@@ -10,53 +10,10 @@ import {
import { mockConvexReactClient } from "@common/lib/mockConvexReactClient";
import {
ConnectedDeploymentContext,
- DeploymentInfo,
DeploymentInfoContext,
} from "@common/lib/deploymentContext";
import { ProjectEnvVarConfig } from "@common/features/settings/lib/types";
-
-const deploymentInfo: DeploymentInfo = {
- ok: true,
- deploymentUrl: process.env.NEXT_PUBLIC_DEPLOYMENT_URL!,
- adminKey: process.env.NEXT_PUBLIC_ADMIN_KEY!,
- useCurrentTeam: () => ({
- id: 0,
- name: "Team",
- slug: "team",
- }),
- useTeamMembers: () => [],
- useTeamEntitlements: () => ({
- auditLogsEnabled: true,
- }),
- useCurrentUsageBanner: () => null,
- useCurrentProject: () => ({
- id: 0,
- name: "Project",
- slug: "project",
- teamId: 0,
- }),
- useCurrentDeployment: () => ({
- id: 0,
- name: "local",
- deploymentType: "prod",
- projectId: 0,
- kind: "local",
- previewIdentifier: null,
- }),
- useHasProjectAdminPermissions: () => true,
- useIsDeploymentPaused: () => false,
- useProjectEnvironmentVariables: () => ({ configs: [] }),
- CloudImport: ({ sourceCloudBackupId }: { sourceCloudBackupId: number }) => (
- {sourceCloudBackupId}
- ),
- useLogDeploymentEvent: () => () => {},
- TeamMemberLink: () => ,
- useTeamUsageState: () => "Default",
- teamsURI: "/",
- projectsURI: "/",
- deploymentsURI: "/",
- isSelfHosted: true,
-};
+import { mockDeploymentInfo } from "@common/lib/mockDeploymentInfo";
const createEnvironmentVariable = (
name: string,
@@ -221,7 +178,7 @@ describe("Prefilling env var name", () => {
function renderUI() {
render(
-
+
Promise {
const deploymentUrl = useDeploymentUrl();
const adminKey = useAdminKey();
+ const { reportHttpError } = useContext(DeploymentInfoContext);
return async (changes) => {
const body = JSON.stringify({ changes });
const res = await fetch(
@@ -40,6 +44,7 @@ export function useUpdateEnvVars(): (
export function useDeleteComponent() {
const deploymentUrl = useDeploymentUrl();
const adminKey = useAdminKey();
+ const { reportHttpError } = useContext(DeploymentInfoContext);
return useCallback(
async (id: Id<"_components">) => {
const res = await fetch(`${deploymentUrl}/api/delete_component`, {
@@ -56,7 +61,7 @@ export function useDeleteComponent() {
toast("error", err.message);
}
},
- [deploymentUrl, adminKey],
+ [deploymentUrl, adminKey, reportHttpError],
);
}
@@ -69,6 +74,7 @@ export function useChangeDeploymentState(): (
}
const deploymentUrl = useDeploymentUrl();
const authHeader = useDeploymentAuthHeader();
+ const { reportHttpError } = useContext(DeploymentInfoContext);
return async (newState) => {
const body = JSON.stringify({ newState });
const res = await fetch(`${deploymentUrl}/api/change_deployment_state`, {
diff --git a/npm-packages/dashboard-common/src/index.ts b/npm-packages/dashboard-common/src/index.ts
index f4f49b91..55a5d82c 100644
--- a/npm-packages/dashboard-common/src/index.ts
+++ b/npm-packages/dashboard-common/src/index.ts
@@ -4,12 +4,7 @@
*/
// General dashboard exports
-export {
- reportHttpError,
- toast,
- dismissToast,
- backoffWithJitter,
-} from "@common/lib/utils";
+export { toast, dismissToast, backoffWithJitter } from "@common/lib/utils";
export * from "@common/lib/fetching";
export * from "@common/lib/useGlobalLocalStorage";
export * from "@common/lib/useCopy";
diff --git a/npm-packages/dashboard-common/src/layouts/SidebarDetailLayout.tsx b/npm-packages/dashboard-common/src/layouts/SidebarDetailLayout.tsx
index b5ca2053..bc837d35 100644
--- a/npm-packages/dashboard-common/src/layouts/SidebarDetailLayout.tsx
+++ b/npm-packages/dashboard-common/src/layouts/SidebarDetailLayout.tsx
@@ -5,7 +5,6 @@ import { ReactNode, useContext, useRef, useState } from "react";
import { useLocalStorage } from "react-use";
import { gt } from "semver";
import udfs from "udfs";
-import { ErrorBoundary } from "@sentry/nextjs";
import { useRouter } from "next/router";
import {
ImperativePanelHandle,
@@ -16,28 +15,12 @@ import {
import { DragHandleDots2Icon } from "@radix-ui/react-icons";
import { cn } from "@common/lib/cn";
-import { Callout } from "@common/elements/Callout";
import { PageContent } from "@common/elements/PageContent";
import { DeploymentInfoContext } from "@common/lib/deploymentContext";
import { Tooltip } from "@common/elements/Tooltip";
import { ClosePanelButton } from "@common/elements/ClosePanelButton";
import { Button } from "@common/elements/Button";
-function Fallback({ error }: { error: Error | null }) {
- return (
-
-
-
-
-
We encountered an error loading this page.
- {error &&
{error.toString()}
}
-
-
-
-
- );
-}
-
export function SidebarDetailLayout({
sidebarComponent,
contentComponent,
@@ -54,6 +37,8 @@ export function SidebarDetailLayout({
const [collapsed, setCollapsed] = useState(false);
const panelRef = useRef(null);
+ const { ErrorBoundary } = useContext(DeploymentInfoContext);
+
return (
-
+
{contentComponent}
diff --git a/npm-packages/dashboard-common/src/lib/deploymentContext.tsx b/npm-packages/dashboard-common/src/lib/deploymentContext.tsx
index e8a08cbc..1a222c0a 100644
--- a/npm-packages/dashboard-common/src/lib/deploymentContext.tsx
+++ b/npm-packages/dashboard-common/src/lib/deploymentContext.tsx
@@ -10,7 +10,6 @@ import {
useState,
} from "react";
import { useRouter } from "next/router";
-import { captureMessage } from "@sentry/nextjs";
import { cn } from "@common/lib/cn";
import { LoadingLogo } from "@common/elements/Loading";
import { ProjectEnvVarConfig } from "@common/features/settings/lib/types";
@@ -25,6 +24,13 @@ export type DeploymentInfo = (
}
| { ok: false; errorCode: string; errorMessage: string }
) & {
+ captureMessage: (msg: string) => void;
+ captureException: (e: any) => void;
+ reportHttpError: (
+ method: string,
+ url: string,
+ error: { code: string; message: string },
+ ) => void;
useCurrentTeam():
| {
id: number;
@@ -73,6 +79,7 @@ export type DeploymentInfo = (
memberId?: number | null;
name: string;
}): JSX.Element;
+ ErrorBoundary(props: { children: ReactNode }): JSX.Element;
teamsURI: string;
projectsURI: string;
deploymentsURI: string;
@@ -247,6 +254,8 @@ export function DeploymentApiProvider({
}
}, [router.isReady, router.query, deploymentOverride]);
+ const deploymentInfoContext = useContext(DeploymentInfoContext);
+
const connected = useConnectedDeployment(deploymentName);
// eslint-disable-next-line react/jsx-no-constructed-context-values
let value: MaybeConnectedDeployment = {
@@ -273,7 +282,7 @@ export function DeploymentApiProvider({
errorKind: "DoesNotExist",
};
} else if (connected && !connected?.ok) {
- captureMessage(
+ deploymentInfoContext?.captureMessage(
`Can't connect to deployment ${connected?.errorCode} ${connected?.errorMessage}`,
);
}
diff --git a/npm-packages/dashboard-common/src/lib/functions/FunctionsProvider.tsx b/npm-packages/dashboard-common/src/lib/functions/FunctionsProvider.tsx
index 7526705c..01b4c1a6 100644
--- a/npm-packages/dashboard-common/src/lib/functions/FunctionsProvider.tsx
+++ b/npm-packages/dashboard-common/src/lib/functions/FunctionsProvider.tsx
@@ -1,7 +1,6 @@
-import { ReactNode, useMemo } from "react";
+import { ReactNode, useContext, useMemo } from "react";
import { useRouter } from "next/router";
import { cn } from "@common/lib/cn";
-import { captureMessage } from "@sentry/nextjs";
import { Module } from "system-udfs/convex/_system/frontend/common";
import { createGlobalState } from "react-use";
import {
@@ -15,6 +14,7 @@ import { useListModulesAllNents } from "@common/lib/functions/useListModules";
import { createContextHook } from "@common/lib/createContextHook";
import { ComponentId, Nent, useNents } from "@common/lib/useNents";
import { LoadingLogo } from "@common/elements/Loading";
+import { DeploymentInfoContext } from "@common/lib/deploymentContext";
const [FunctionsContext, useFunctions] = createContextHook<
Map>
@@ -149,6 +149,7 @@ export function useCurrentOpenFunction() {
export function useModuleFunctions(): ModuleFunction[] {
const modules = useFunctions();
const { nents } = useNents();
+ const { captureMessage } = useContext(DeploymentInfoContext);
return useMemo(() => {
if (!nents) {
@@ -159,7 +160,7 @@ export function useModuleFunctions(): ModuleFunction[] {
}
return modulesToModuleFunctions(modules, nents);
- }, [modules, nents]);
+ }, [captureMessage, modules, nents]);
}
// Exported for testing only
@@ -190,6 +191,7 @@ export function useRootEntries() {
const modules = useFunctions();
const [searchTerm] = useFunctionSearchTerm();
const { selectedNent, nents } = useNents();
+ const { captureMessage } = useContext(DeploymentInfoContext);
const rootEntries = useMemo(() => {
if (!nents) {
@@ -221,7 +223,7 @@ export function useRootEntries() {
}
return rootDirectory?.children ?? [];
- }, [modules, nents, searchTerm, selectedNent]);
+ }, [captureMessage, modules, nents, searchTerm, selectedNent]);
return rootEntries;
}
diff --git a/npm-packages/dashboard-common/src/lib/mockDeploymentInfo.tsx b/npm-packages/dashboard-common/src/lib/mockDeploymentInfo.tsx
new file mode 100644
index 00000000..f6957c7f
--- /dev/null
+++ b/npm-packages/dashboard-common/src/lib/mockDeploymentInfo.tsx
@@ -0,0 +1,50 @@
+import { DeploymentInfo } from "@common/lib/deploymentContext";
+
+export const mockDeploymentInfo: DeploymentInfo = {
+ ok: true,
+ reportHttpError: jest.fn(),
+ captureException: jest.fn(),
+ captureMessage: jest.fn(),
+ deploymentUrl: process.env.NEXT_PUBLIC_DEPLOYMENT_URL!,
+ adminKey: process.env.NEXT_PUBLIC_ADMIN_KEY!,
+ useCurrentTeam: () => ({
+ id: 0,
+ name: "Team",
+ slug: "team",
+ }),
+ useTeamMembers: () => [],
+ useTeamEntitlements: () => ({
+ auditLogsEnabled: true,
+ }),
+ useCurrentUsageBanner: () => null,
+ useCurrentProject: () => ({
+ id: 0,
+ name: "Project",
+ slug: "project",
+ teamId: 0,
+ }),
+ useLogDeploymentEvent: () => () => {},
+ useCurrentDeployment: () => ({
+ id: 0,
+ name: "local",
+ deploymentType: "prod",
+ projectId: 0,
+ kind: "local",
+ previewIdentifier: null,
+ }),
+ useHasProjectAdminPermissions: () => true,
+ useIsDeploymentPaused: () => false,
+ useProjectEnvironmentVariables: () => ({ configs: [] }),
+ CloudImport: ({ sourceCloudBackupId }: { sourceCloudBackupId: number }) => (
+ {sourceCloudBackupId}
+ ),
+ ErrorBoundary: ({ children }: { children: React.ReactNode }) => (
+ <>{children}>
+ ),
+ TeamMemberLink: () => ,
+ useTeamUsageState: () => "Default",
+ teamsURI: "/",
+ projectsURI: "/",
+ deploymentsURI: "/",
+ isSelfHosted: true,
+};
diff --git a/npm-packages/dashboard-common/src/lib/useDeploymentAuditLog.ts b/npm-packages/dashboard-common/src/lib/useDeploymentAuditLog.ts
index e84e7ff7..d4efe258 100644
--- a/npm-packages/dashboard-common/src/lib/useDeploymentAuditLog.ts
+++ b/npm-packages/dashboard-common/src/lib/useDeploymentAuditLog.ts
@@ -1,4 +1,3 @@
-import * as Sentry from "@sentry/nextjs";
import { usePaginatedQuery } from "convex/react";
import udfs from "udfs";
import { Doc } from "system-udfs/convex/_generated/dataModel";
@@ -50,12 +49,6 @@ function processDeploymentAuditLogEvent(
case "snapshot_import":
break;
default:
- // eslint-disable-next-line no-case-declarations, @typescript-eslint/no-unused-vars
- Sentry.captureMessage(
- `Unexpected deployment audit log with action ${
- (auditLogEvent as any).action
- }`,
- );
return null;
}
diff --git a/npm-packages/dashboard-common/src/lib/useLogs.ts b/npm-packages/dashboard-common/src/lib/useLogs.ts
index 0793dbdf..2a1a8d29 100644
--- a/npm-packages/dashboard-common/src/lib/useLogs.ts
+++ b/npm-packages/dashboard-common/src/lib/useLogs.ts
@@ -1,5 +1,4 @@
import { useCallback, useEffect, useState } from "react";
-import { captureException } from "@sentry/nextjs";
import {
FunctionExecution,
UdfType,
@@ -221,7 +220,6 @@ function queryFunctionLogs(
if (e instanceof DOMException && e.code === DOMException.ABORT_ERR) {
return;
}
- captureException(e);
numFailures += 1;
// Give it some time before we show an error to avoid looking extra broken due to transient
// connectivity or backend errors (e.g. a backend restart during a push).
diff --git a/npm-packages/dashboard-common/src/lib/utils.ts b/npm-packages/dashboard-common/src/lib/utils.ts
index 201eafb9..d907101c 100644
--- a/npm-packages/dashboard-common/src/lib/utils.ts
+++ b/npm-packages/dashboard-common/src/lib/utils.ts
@@ -4,7 +4,6 @@ import { toast as sonnerToast } from "sonner";
import * as IdEncoding from "id-encoding";
import { NextRouter } from "next/router";
import { FilterExpression } from "system-udfs/convex/_system/frontend/lib/filters";
-import { captureMessage } from "@sentry/nextjs";
export function dismissToast(id: string) {
sonnerToast.dismiss(id);
@@ -105,13 +104,3 @@ export function documentHref(
},
};
}
-
-export const reportHttpError = (
- method: string,
- url: string,
- error: { code: string; message: string },
-) => {
- captureMessage(
- `failed to request ${method} ${url}: ${error.code} - ${error.message} `,
- );
-};
diff --git a/npm-packages/dashboard-self-hosted/src/components/ErrorBoundary.tsx b/npm-packages/dashboard-self-hosted/src/components/ErrorBoundary.tsx
new file mode 100644
index 00000000..a4bd51fe
--- /dev/null
+++ b/npm-packages/dashboard-self-hosted/src/components/ErrorBoundary.tsx
@@ -0,0 +1,69 @@
+import { ExitIcon } from "@radix-ui/react-icons";
+import React, { ReactNode, ErrorInfo } from "react";
+import { Button } from "@common/elements/Button";
+import { Sheet } from "@common/elements/Sheet";
+
+interface ErrorBoundaryProps {
+ children: ReactNode;
+}
+
+interface ErrorBoundaryState {
+ error?: Error;
+}
+
+export class ErrorBoundary extends React.Component<
+ ErrorBoundaryProps,
+ ErrorBoundaryState
+> {
+ constructor(props: ErrorBoundaryProps) {
+ super(props);
+ this.state = {};
+ }
+
+ static getDerivedStateFromError(e: Error): ErrorBoundaryState {
+ return { error: e };
+ }
+
+ componentDidCatch(error: Error, errorInfo: ErrorInfo) {
+ console.error("Uncaught error:", error, errorInfo);
+ }
+
+ render() {
+ const { error } = this.state;
+ const { children } = this.props;
+ if (error) {
+ return (
+
+
Something went wrong
+
+ {error.message.includes("not permitted") && (
+
+ Your admin key may be invalid. Please try logging in again.
+
+ )}
+
}
+ size="xs"
+ onClick={() => {
+ window.sessionStorage.setItem("adminKey", "");
+ window.location.reload();
+ }}
+ variant="neutral"
+ >
+ Log Out
+
+
+
+ {error.message}
+
+ {error.stack}
+
+
+
+ );
+ }
+
+ return children;
+ }
+}
diff --git a/npm-packages/dashboard-self-hosted/src/pages/_app.tsx b/npm-packages/dashboard-self-hosted/src/pages/_app.tsx
index c3394ed6..9210de5b 100644
--- a/npm-packages/dashboard-self-hosted/src/pages/_app.tsx
+++ b/npm-packages/dashboard-self-hosted/src/pages/_app.tsx
@@ -28,15 +28,9 @@ import {
MenuItem,
TextInput,
Button,
- Sheet,
} from "dashboard-common";
-import React, {
- ErrorInfo,
- ReactNode,
- useEffect,
- useMemo,
- useState,
-} from "react";
+import React, { useEffect, useMemo, useState } from "react";
+import { ErrorBoundary } from "components/ErrorBoundary";
function App({
Component,
@@ -101,6 +95,17 @@ export default App;
const deploymentInfo: Omit = {
ok: true,
+ captureMessage: console.error,
+ captureException: console.error,
+ reportHttpError: (
+ method: string,
+ url: string,
+ error: { code: string; message: string },
+ ) => {
+ console.error(
+ `failed to request ${method} ${url}: ${error.code} - ${error.message} `,
+ );
+ },
useCurrentTeam: () => ({
id: 0,
name: "Team",
@@ -137,6 +142,9 @@ const deploymentInfo: Omit = {
{sourceCloudBackupId}
),
TeamMemberLink: () => ,
+ ErrorBoundary: ({ children }: { children: React.ReactNode }) => (
+ {children}
+ ),
useTeamUsageState: () => "Default",
teamsURI: "/",
projectsURI: "/",
@@ -245,68 +253,3 @@ function Header({ onLogout }: { onLogout: () => void }) {
);
}
-
-interface ErrorBoundaryProps {
- children: ReactNode;
-}
-
-interface ErrorBoundaryState {
- error?: Error;
-}
-
-class ErrorBoundary extends React.Component<
- ErrorBoundaryProps,
- ErrorBoundaryState
-> {
- constructor(props: ErrorBoundaryProps) {
- super(props);
- this.state = {};
- }
-
- static getDerivedStateFromError(e: Error): ErrorBoundaryState {
- return { error: e };
- }
-
- componentDidCatch(error: Error, errorInfo: ErrorInfo) {
- console.error("Uncaught error:", error, errorInfo);
- }
-
- render() {
- const { error } = this.state;
- const { children } = this.props;
- if (error) {
- return (
-
-
Something went wrong
-
- {error.message.includes("not permitted") && (
-
- Your admin key may be invalid. Please try logging in again.
-
- )}
-
}
- size="xs"
- onClick={() => {
- window.sessionStorage.setItem("adminKey", "");
- window.location.reload();
- }}
- variant="neutral"
- >
- Log Out
-
-
-
- {error.message}
-
- {error.stack}
-
-
-
- );
- }
-
- return children;
- }
-}
diff --git a/npm-packages/dashboard/package.json b/npm-packages/dashboard/package.json
index f68acb4e..113388e2 100644
--- a/npm-packages/dashboard/package.json
+++ b/npm-packages/dashboard/package.json
@@ -39,17 +39,14 @@
"date-fns": "^2.29.3",
"fetch-retry": "~6.0.0",
"formik": "~2.2.9",
- "js-base64": "~3.7.4",
"launchdarkly-react-client-sdk": "^3.0.4",
"lodash": "~4.17.21",
"next": "14.2.21",
"nprogress": "~0.2.0",
- "prop-types": "~15.8.1",
"react": "^18.0.0",
"react-day-picker": "~8.10.1",
"react-dom": "^18.0.0",
"react-popper": "^2.3.0",
- "react-table": "7.8.0",
"react-use": "~17.6.0",
"react-window": "1.8.11",
"recharts": "2.15.0",
diff --git a/npm-packages/dashboard/src/api/api.ts b/npm-packages/dashboard/src/api/api.ts
index 3fb6b787..b05a7b76 100644
--- a/npm-packages/dashboard/src/api/api.ts
+++ b/npm-packages/dashboard/src/api/api.ts
@@ -8,14 +8,14 @@ import { PathsWithMethod } from "openapi-typescript-helpers";
import { createMutateHook, createQueryHook } from "swr-openapi";
import isMatch from "lodash/isMatch";
import { fireGoogleAnalyticsEvent } from "elements/GoogleAnalytics";
-import { reportHttpError, toast } from "dashboard-common";
+import { toast } from "dashboard-common";
import type { paths as BigBrainPaths } from "generatedApi";
import { SWRConfiguration } from "swr";
import { useAccessToken } from "hooks/useServerSideData";
import { useRouter } from "next/router";
import { useCallback, useEffect } from "react";
import { usePrevious } from "react-use";
-import { getGoogleAnalyticsClientId } from "../hooks/fetching";
+import { getGoogleAnalyticsClientId, reportHttpError } from "../hooks/fetching";
export const client = createClient({
baseUrl: `${process.env.NEXT_PUBLIC_BIG_BRAIN_URL}/api/dashboard`,
diff --git a/npm-packages/dashboard/src/hooks/api.tsx b/npm-packages/dashboard/src/hooks/api.tsx
index 1c9c4c29..0beb4e1b 100644
--- a/npm-packages/dashboard/src/hooks/api.tsx
+++ b/npm-packages/dashboard/src/hooks/api.tsx
@@ -53,7 +53,6 @@ export function useUpdateProjectEnvVars(projectId?: number) {
url: `/api/dashboard/projects/${projectId}/environment_variables/update_batch`,
mutateKey: `/api/dashboard/projects/${projectId}/environment_variables/list`,
successToast: "Environment variables updated.",
- method: "POST",
});
}
diff --git a/npm-packages/dashboard/src/hooks/debug.ts b/npm-packages/dashboard/src/hooks/debug.ts
deleted file mode 100644
index be1c3fb6..00000000
--- a/npm-packages/dashboard/src/hooks/debug.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { useRef } from "react";
-
-const firstRender = {};
-export function useWhatChanged(obj: Record, label?: string) {
- const previous = useRef>(firstRender);
-
- if (previous.current === firstRender) {
- previous.current = obj;
- }
-
- if (Object.keys(obj).length !== Object.keys(previous.current).length) {
- // eslint-disable-next-line no-console
- console.log("changed size!");
- previous.current = obj;
- return;
- }
-
- const changes: Record = {};
- // eslint-disable-next-line no-restricted-syntax
- for (const prop of Object.keys(obj)) {
- const prev = previous.current[prop];
- const cur = obj[prop];
- if (cur !== prev) {
- changes[prop] = [prev, cur];
- }
- }
-
- if (Object.keys(changes).length) {
- // eslint-disable-next-line no-console
- console.log(`${label || "component"}rerendered from change:`, changes);
- }
-
- previous.current = obj;
-}
diff --git a/npm-packages/dashboard/src/hooks/deploymentApi.tsx b/npm-packages/dashboard/src/hooks/deploymentApi.tsx
index 4e19a22e..7fbbd944 100644
--- a/npm-packages/dashboard/src/hooks/deploymentApi.tsx
+++ b/npm-packages/dashboard/src/hooks/deploymentApi.tsx
@@ -12,7 +12,6 @@ import {
ConnectedDeploymentContext,
DeploymentInfoContext,
DeploymentInfo,
- reportHttpError,
useAdminKey,
useDeploymentAuthHeader,
useDeploymentUrl,
@@ -33,6 +32,13 @@ import { useIsDeploymentPaused } from "hooks/useIsDeploymentPaused";
import { CloudImport } from "elements/BackupIdentifier";
import { TeamMemberLink } from "elements/TeamMemberLink";
import { logDeploymentEvent } from "convex-analytics";
+import {
+ ErrorBoundary,
+ captureException,
+ captureMessage,
+} from "@sentry/nextjs";
+import { reportHttpError } from "hooks/fetching";
+import { Fallback } from "pages/500";
import { useAccessToken } from "./useServerSideData";
import { useCurrentProject } from "../api/projects";
import { useTeamUsageState } from "./useTeamUsageState";
@@ -42,6 +48,10 @@ import { useProjectEnvironmentVariables } from "./api";
const useIsomorphicLayoutEffect =
typeof window !== "undefined" ? useLayoutEffect : useEffect;
+function DeploymentErrorBoundary({ children }: { children: React.ReactNode }) {
+ return {children};
+}
+
export function DeploymentInfoProvider({
children,
deploymentOverride,
@@ -69,6 +79,9 @@ export function DeploymentInfoProvider({
);
setDeploymentInfo({
...info,
+ captureMessage,
+ captureException,
+ reportHttpError,
useCurrentTeam,
useCurrentProject,
useCurrentUsageBanner,
@@ -82,6 +95,7 @@ export function DeploymentInfoProvider({
useLogDeploymentEvent,
TeamMemberLink,
CloudImport,
+ ErrorBoundary: DeploymentErrorBoundary,
teamsURI,
projectsURI,
deploymentsURI,
diff --git a/npm-packages/dashboard/src/hooks/fetching.ts b/npm-packages/dashboard/src/hooks/fetching.ts
index 31624fd5..ff7d87b6 100644
--- a/npm-packages/dashboard/src/hooks/fetching.ts
+++ b/npm-packages/dashboard/src/hooks/fetching.ts
@@ -1,5 +1,5 @@
import { Middleware } from "swr";
-import { captureException } from "@sentry/nextjs";
+import { captureException, captureMessage } from "@sentry/nextjs";
import { deploymentFetch, translateResponse } from "dashboard-common";
import { useAccessToken } from "./useServerSideData";
@@ -100,3 +100,13 @@ export const bigBrainAuth: Middleware =
return useSWRNext(swrKey, fetcher, { ...config, fallbackData });
};
+
+export const reportHttpError = (
+ method: string,
+ url: string,
+ error: { code: string; message: string },
+) => {
+ captureMessage(
+ `failed to request ${method} ${url}: ${error.code} - ${error.message} `,
+ );
+};
diff --git a/npm-packages/dashboard/src/hooks/useMutation.ts b/npm-packages/dashboard/src/hooks/useMutation.ts
index 7af3f12a..e3c84df4 100644
--- a/npm-packages/dashboard/src/hooks/useMutation.ts
+++ b/npm-packages/dashboard/src/hooks/useMutation.ts
@@ -1,31 +1,22 @@
-import { reportHttpError, toast } from "dashboard-common";
+import { toast } from "dashboard-common";
import { useCallback } from "react";
import { useSWRConfig } from "swr";
-import { fireGoogleAnalyticsEvent } from "elements/GoogleAnalytics";
-import { useRouter } from "next/router";
-import { useAuthHeader } from "./fetching";
+import { reportHttpError, useAuthHeader } from "./fetching";
type MutateOptions = {
url: string;
mutateKey?: string;
successToast?: string;
- method?: "POST" | "PUT";
toastOnError?: boolean;
- redirectTo?: string;
- googleAnalyticsEvent?: string;
};
// Makes a mutative API request, handling errors and toasts.
export function useMutation({
url,
- method = "POST",
mutateKey,
successToast,
toastOnError = true,
- googleAnalyticsEvent,
- redirectTo,
}: MutateOptions): (body: Request) => Promise {
- const router = useRouter();
const { mutate } = useSWRConfig();
const authHeader = useAuthHeader();
return useCallback(
@@ -41,7 +32,7 @@ export function useMutation({
const response = await fetch(
`${process.env.NEXT_PUBLIC_BIG_BRAIN_URL}${url}`,
{
- method,
+ method: "POST",
headers: {
Authorization: authHeader,
"Content-Type": "application/json",
@@ -51,40 +42,15 @@ export function useMutation({
);
if (!response.ok) {
const error = await response.json();
- reportHttpError(method, url, error);
+ reportHttpError("POST", url, error);
toastOnError && toast("error", error.message, error.message);
throw error;
} else {
- redirectTo && (await router.push(redirectTo));
mutateKey && (await mutate([mutateKey, authHeader]));
successToast && toast("success", successToast);
- googleAnalyticsEvent && fireGoogleAnalyticsEvent(googleAnalyticsEvent);
return response;
}
},
- [
- authHeader,
- googleAnalyticsEvent,
- method,
- mutate,
- mutateKey,
- redirectTo,
- router,
- successToast,
- toastOnError,
- url,
- ],
- );
-}
-
-// Convenience wrapper for converting the JSON response to a parameterized `Response` type.
-export function useMutationWithResponse(
- options: MutateOptions,
-): (body: Request) => Promise {
- const mutate = useMutation(options);
- return useCallback(
- async (request: Request): Promise =>
- (await mutate(request)).json(),
- [mutate],
+ [authHeader, mutate, mutateKey, successToast, toastOnError, url],
);
}