feat(web): gate settings nav by workspace role (MS21-RBAC-001) (#579)
All checks were successful
ci/woodpecker/push/web Pipeline was successful
All checks were successful
ci/woodpecker/push/web Pipeline was successful
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #579.
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import type { ReactElement, ReactNode } from "react";
|
||||
import { WorkspaceMemberRole } from "@mosaic/shared";
|
||||
import { useAuth } from "@/lib/auth/auth-context";
|
||||
import { fetchUserWorkspaces } from "@/lib/api/workspaces";
|
||||
import Link from "next/link";
|
||||
|
||||
interface CategoryConfig {
|
||||
@@ -11,6 +14,7 @@ interface CategoryConfig {
|
||||
accent: string;
|
||||
iconBg: string;
|
||||
icon: ReactNode;
|
||||
adminOnly?: boolean;
|
||||
}
|
||||
|
||||
interface SettingsCategoryCardProps {
|
||||
@@ -200,6 +204,7 @@ const categories: CategoryConfig[] = [
|
||||
title: "Users",
|
||||
description: "Invite, manage roles, and deactivate users across your workspaces.",
|
||||
href: "/settings/users",
|
||||
adminOnly: true,
|
||||
accent: "var(--ms-green-400)",
|
||||
iconBg: "rgba(34, 197, 94, 0.12)",
|
||||
icon: (
|
||||
@@ -277,7 +282,30 @@ const categories: CategoryConfig[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const ADMIN_ROLES: WorkspaceMemberRole[] = [WorkspaceMemberRole.OWNER, WorkspaceMemberRole.ADMIN];
|
||||
|
||||
export default function SettingsPage(): ReactElement {
|
||||
const { user } = useAuth();
|
||||
const [isAdmin, setIsAdmin] = useState<boolean>(false);
|
||||
|
||||
const checkRole = useCallback(async (): Promise<void> => {
|
||||
if (user === null) return;
|
||||
try {
|
||||
const workspaces = await fetchUserWorkspaces();
|
||||
const hasAdminRole = workspaces.some((ws) => ADMIN_ROLES.includes(ws.role));
|
||||
setIsAdmin(hasAdminRole);
|
||||
} catch {
|
||||
// Fail open — show all items if we can't determine role
|
||||
setIsAdmin(true);
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
useEffect(() => {
|
||||
void checkRole();
|
||||
}, [checkRole]);
|
||||
|
||||
const visibleCategories = categories.filter((c) => c.adminOnly !== true || isAdmin);
|
||||
|
||||
return (
|
||||
<div className="max-w-6xl mx-auto p-6">
|
||||
{/* Page header */}
|
||||
@@ -305,7 +333,7 @@ export default function SettingsPage(): ReactElement {
|
||||
|
||||
{/* Category grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{categories.map((category) => (
|
||||
{visibleCategories.map((category) => (
|
||||
<SettingsCategoryCard key={category.href} category={category} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user