From fb15f8cde6d96e43d9b986b23cd587808188c129 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Mon, 19 Jan 2026 21:57:28 -0800 Subject: [PATCH] add placeholder approvals ui --- messages/en-US.json | 10 +- .../(private)/access/approvals/page.tsx | 21 ++- src/components/ApprovalFeed.tsx | 12 +- src/components/ApprovalsEmptyState.tsx | 126 ++++++++++++++++++ 4 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 src/components/ApprovalsEmptyState.tsx diff --git a/messages/en-US.json b/messages/en-US.json index 34abc5f5..827b8187 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -2514,5 +2514,13 @@ "deviceActionsDescription": "Manage device status and access", "devicePendingApprovalBannerDescription": "This device is pending approval. It won't be able to connect to resources until approved.", "connected": "Connected", - "disconnected": "Disconnected" + "disconnected": "Disconnected", + "approvalsEmptyStateTitle": "Device Approvals Not Enabled", + "approvalsEmptyStateDescription": "Enable device approvals for roles to require admin approval before users can connect new devices.", + "approvalsEmptyStateStep1Title": "Go to Roles", + "approvalsEmptyStateStep1Description": "Navigate to your organization's roles settings to configure device approvals.", + "approvalsEmptyStateStep2Title": "Enable Device Approvals", + "approvalsEmptyStateStep2Description": "Edit a role and enable the 'Require Device Approvals' option. Users with this role will need admin approval for new devices.", + "approvalsEmptyStatePreviewDescription": "Preview: When enabled, pending device requests will appear here for review", + "approvalsEmptyStateButtonText": "Manage Roles" } diff --git a/src/app/[orgId]/settings/(private)/access/approvals/page.tsx b/src/app/[orgId]/settings/(private)/access/approvals/page.tsx index 9e89a901..ad6e717b 100644 --- a/src/app/[orgId]/settings/(private)/access/approvals/page.tsx +++ b/src/app/[orgId]/settings/(private)/access/approvals/page.tsx @@ -8,6 +8,7 @@ import { getCachedOrg } from "@app/lib/api/getCachedOrg"; import type { ApprovalItem } from "@app/lib/queries"; import OrgProvider from "@app/providers/OrgProvider"; import type { GetOrgResponse } from "@server/routers/org"; +import type { ListRolesResponse } from "@server/routers/role"; import type { AxiosResponse } from "axios"; import { getTranslations } from "next-intl/server"; @@ -36,6 +37,21 @@ export default async function ApprovalFeedPage(props: ApprovalFeedPageProps) { org = orgRes.data.data; } + // Fetch roles to check if approvals are enabled + let hasApprovalsEnabled = false; + const rolesRes = await internal + .get>( + `/org/${params.orgId}/roles`, + await authCookieHeader() + ) + .catch((e) => {}); + + if (rolesRes && rolesRes.status === 200) { + hasApprovalsEnabled = rolesRes.data.data.roles.some( + (role) => role.requireDeviceApproval === true + ); + } + const t = await getTranslations(); return ( @@ -51,7 +67,10 @@ export default async function ApprovalFeedPage(props: ApprovalFeedPageProps) {
- +
diff --git a/src/components/ApprovalFeed.tsx b/src/components/ApprovalFeed.tsx index e982f82d..4c6122c6 100644 --- a/src/components/ApprovalFeed.tsx +++ b/src/components/ApprovalFeed.tsx @@ -29,12 +29,17 @@ import { } from "./ui/select"; import { Separator } from "./ui/separator"; import { InfoPopup } from "./ui/info-popup"; +import { ApprovalsEmptyState } from "./ApprovalsEmptyState"; export type ApprovalFeedProps = { orgId: string; + hasApprovalsEnabled: boolean; }; -export function ApprovalFeed({ orgId }: ApprovalFeedProps) { +export function ApprovalFeed({ + orgId, + hasApprovalsEnabled +}: ApprovalFeedProps) { const searchParams = useSearchParams(); const path = usePathname(); const t = useTranslations(); @@ -51,6 +56,11 @@ export function ApprovalFeed({ orgId }: ApprovalFeedProps) { const approvals = data?.approvals ?? []; + // Show empty state if no approvals are enabled for any role + if (!hasApprovalsEnabled) { + return ; + } + return (
diff --git a/src/components/ApprovalsEmptyState.tsx b/src/components/ApprovalsEmptyState.tsx new file mode 100644 index 00000000..421af2f0 --- /dev/null +++ b/src/components/ApprovalsEmptyState.tsx @@ -0,0 +1,126 @@ +"use client"; + +import { Button } from "@app/components/ui/button"; +import { Card, CardContent } from "@app/components/ui/card"; +import { + ShieldCheck, + Check, + Ban, + User, + Settings, + ArrowRight +} from "lucide-react"; +import { useTranslations } from "next-intl"; +import Link from "next/link"; + +type ApprovalsEmptyStateProps = { + orgId: string; +}; + +export function ApprovalsEmptyState({ orgId }: ApprovalsEmptyStateProps) { + const t = useTranslations(); + + return ( +
+ + +
+
+ +
+ +
+

+ {t("approvalsEmptyStateTitle")} +

+

+ {t("approvalsEmptyStateDescription")} +

+
+ +
+
+
+
+ +
+
+

+ {t("approvalsEmptyStateStep1Title")} +

+

+ {t( + "approvalsEmptyStateStep1Description" + )} +

+
+
+ +
+
+ +
+
+

+ {t("approvalsEmptyStateStep2Title")} +

+

+ {t( + "approvalsEmptyStateStep2Description" + )} +

+
+
+
+ + {/* Abstract UI Preview */} +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ + + + +
+
+
+
+ ); +}