feat: apply the explicit no-access message to campaigns, contacts, analytics, and API keys pages too, so every permission-gated tab says which permission is missing instead of showing nothing

This commit is contained in:
Matthew Meszaros
2026-06-11 13:21:34 +02:00
parent 72d2af2c3d
commit babde3397c
4 changed files with 20 additions and 1 deletions
+5
View File
@@ -1,3 +1,5 @@
import { NoAccess } from "@/components/layout/NoAccess";
import { usePermission } from "@/hooks/usePermission";
import { useMemo, useState } from "react";
import { Link } from "react-router-dom";
import {
@@ -49,6 +51,7 @@ function num(v: number | undefined): string {
}
export default function AnalyticsPage() {
const canView = usePermission("VIEW_ANALYTICS");
const [range, setRange] = useState<Range>("7d");
const [metric, setMetric] = useState<Metric>("sent");
const dash = useDashboard(range);
@@ -80,6 +83,8 @@ export default function AnalyticsPage() {
daily: (d?.daily_trend ?? []).map((p) => ({ label: p.date, value: p.sent })),
};
if (!canView) return <NoAccess feature="analytics" permissionLabel="View analytics" />;
return (
<Page>
<PageTopbar eyebrow="Analytics" subtitle="Deliverability across the workspace">
+5
View File
@@ -17,6 +17,8 @@
"use client";
import { NoAccess } from "@/components/layout/NoAccess";
import { usePermission } from "@/hooks/usePermission";
import React from "react";
import {
ActivityIcon,
@@ -51,6 +53,7 @@ import KeyDetailDrawer from "./_components/KeyDetailDrawer";
import { StackedBars } from "./_components/Sparkline";
export default function APIKeysPage() {
const canManage = usePermission("MANAGE_API_KEYS");
const summary = useAPIKeyUsageSummary();
const keys = useAPIKeys();
const analytics = useAPIKeyAnalytics("all");
@@ -71,6 +74,8 @@ export default function APIKeysPage() {
const samplePrefix = all[0]?.key_prefix ? `${all[0].key_prefix}` : "wmbly_••";
if (!canManage) return <NoAccess feature="API keys" permissionLabel="Manage API keys" />;
return (
<Page>
<PageTopbar eyebrow="API keys" subtitle="Programmatic access · stripe-style scoped tokens">
+5
View File
@@ -1,3 +1,5 @@
import { NoAccess } from "@/components/layout/NoAccess";
import { usePermission } from "@/hooks/usePermission";
import { useUserProfile } from "@/hooks/context/user";
import useCampaigns from "@/lib/api/hooks/app/campaigns/useCampaigns";
import useStartCampaign from "@/lib/api/hooks/app/campaigns/useStartCampaign";
@@ -252,6 +254,7 @@ function CampaignFolderMenu({ campaign, folders }: { campaign: Campaign; folders
export default function CampaignsPage() {
const p = useUserProfile();
const confirm = useConfirm();
const canView = usePermission("VIEW_CAMPAIGNS");
const startCampaign = useStartCampaign();
const stopCampaign = useStopCampaign();
const [folder, setFolder] = useState<string>("");
@@ -310,6 +313,8 @@ export default function CampaignsPage() {
return stats;
}, [campaigns]);
if (!canView) return <NoAccess feature="campaigns" permissionLabel="View campaigns" />;
return (
<Page>
<PageTopbar
+5 -1
View File
@@ -1,5 +1,9 @@
import ContactsTable from "@/components/app/contacts/ContactsTable";
import { NoAccess } from "@/components/layout/NoAccess";
import { usePermission } from "@/hooks/usePermission";
export default function ContactsPage() {
return (<ContactsTable />)
const canView = usePermission("VIEW_CONTACTS");
if (!canView) return <NoAccess feature="contacts" permissionLabel="View contacts" />;
return <ContactsTable />;
}