+ Project {projectId} has no analytics configuration yet. Add a
+ server key and a client key to start tracking user interactions,
+ visits, clicks, and custom events using Juno.
+
+
+
+
+ ) : (
+ {
+ const { serverAnalyticsKey, clientAnalyticsKey } = keys;
+ updateAnalyticsConfigHandler.mutate({
+ projectId,
+ serverAnalyticsKey,
+ clientAnalyticsKey,
+ });
+ setIsAddConfigDialogOpen(false);
},
- ) => {
- const { serverAnalyticsKey, clientAnalyticsKey } = keys;
- updateAnalyticsConfigHandler.mutate({
- projectId,
- serverAnalyticsKey,
- clientAnalyticsKey,
- });
- setIsAddConfigDialogOpen(false);
- },
- isReadOnly,
- )}
- isLoading={isLoading}
- filterParams={{
- placeholder: "Filter by environment...",
- filterColumn: "environment",
- }}
- onAddNewRow={() => {
- if (analyticsConfigRowData.length === 0) {
- setIsAddConfigDialogOpen(true);
- } else {
- toast.error("Error", {
- description:
- "Project can have at most 1 analytics configuration per environment",
- });
- }
- }}
- onDeleteRow={(rows) => {
- setSelectedRows(rows);
- setIsDeleteDialogOpen(true);
- }}
- />
+ isReadOnly,
+ )}
+ isLoading={isLoading}
+ filterParams={{
+ placeholder: "Filter by environment...",
+ filterColumn: "environment",
+ }}
+ onAddNewRow={() => {
+ if (analyticsConfigRowData.length === 0) {
+ setIsAddConfigDialogOpen(true);
+ } else {
+ toast.error("Error", {
+ description:
+ "Project can have at most 1 analytics configuration per environment",
+ });
+ }
+ }}
+ onDeleteRow={() => {
+ setIsDeleteDialogOpen(true);
+ }}
+ />
+ )}
);
}
diff --git a/src/components/sidebar/project-sidebar.tsx b/src/components/sidebar/project-sidebar.tsx
index c8dd5d5..4f522a3 100644
--- a/src/components/sidebar/project-sidebar.tsx
+++ b/src/components/sidebar/project-sidebar.tsx
@@ -10,6 +10,7 @@ import {
Settings,
Shield,
User,
+ ChartColumn,
} from "lucide-react";
import {
@@ -135,6 +136,11 @@ export function ProjectSidebar({
url: `/projects/${projectId}/settings`,
icon: Settings,
},
+ {
+ title: "Analytics",
+ url: `/projects/${projectId}/analytics`,
+ icon: ChartColumn,
+ },
];
return (
diff --git a/src/components/top-bar.tsx b/src/components/top-bar.tsx
index 1facff1..04c34e5 100644
--- a/src/components/top-bar.tsx
+++ b/src/components/top-bar.tsx
@@ -22,6 +22,7 @@ function getPageTitle(pathname: string): string {
if (sub === "/services/email") return "Email";
if (sub === "/services/files") return "Files";
if (sub === "/settings") return "Settings";
+ if (sub === "/analytics") return "Analytics";
}
return "Dashboard";
diff --git a/src/lib/settings.ts b/src/lib/settings.ts
index 01c8775..d929a6f 100644
--- a/src/lib/settings.ts
+++ b/src/lib/settings.ts
@@ -126,7 +126,7 @@ export async function getEmailConfig(
export async function getAnalyticsConfig(
projectId: string,
-): Promise {
+): Promise {
const session = await getSession();
if (!session) {
throw new Error("Unauthorized");
@@ -147,9 +147,14 @@ export async function getAnalyticsConfig(
);
return JSON.parse(JSON.stringify(analyticsConfig));
} catch (e) {
- if (e.response?.status === 404 || e.response?.status === 401) {
+ if (e.response?.status === 404) {
return null;
}
+ if (e.response?.status === 401) {
+ throw new Error(
+ `Unauthorized: Juno rejected the request for project ${projectId}. The API key may be missing, invalid, or not permitted for this project.`,
+ );
+ }
throw e;
}
}