diff --git a/docs/Permission-Based-UI-Rendering.md b/docs/Permission-Based-UI-Rendering.md
new file mode 100644
index 0000000..d7db8f5
--- /dev/null
+++ b/docs/Permission-Based-UI-Rendering.md
@@ -0,0 +1,661 @@
+# Permission-Based UI Rendering
+
+## Overview
+
+CommDesk uses a permission-based UI rendering system to decide which pages, buttons, actions, and management controls should be visible to a user.
+
+This implementation combines:
+
+- `TanStack Query` for fetching and caching permissions
+- `Redux Toolkit` for storing permission state globally
+- custom permission hooks for clean access checks
+- reusable boundary and gate components for page-level and inline authorization
+
+The goal is simple:
+
+- show only the UI a user is allowed to use
+- keep permission logic centralized
+- make feature code easy to read and easy to extend
+- avoid duplicated permission-check code across screens
+
+This document explains why we use this system, how it works, and how to use it correctly in production code.
+
+---
+
+## Why We Use Permission-Based UI Rendering
+
+### 1. Better security posture at the UI layer
+
+The UI should not invite users to click actions they cannot perform.
+
+Examples:
+
+- hide `Create Event` if the user cannot create events
+- hide `Delete Member` if the user cannot delete members
+- hide `Send Email` if the user does not have contact email access
+
+Important:
+
+UI authorization is not a replacement for backend authorization. The backend must still validate permissions for every protected API action.
+
+### 2. Better user experience
+
+Without permission-aware rendering:
+
+- users see buttons that fail later
+- users get confusing error messages after clicking
+- layout becomes noisy and misleading
+
+With permission-aware rendering:
+
+- the interface feels focused
+- users only see relevant actions
+- restricted areas can provide clear guidance instead of dead ends
+
+### 3. Better maintainability
+
+If every component checks permissions differently, the codebase becomes fragile.
+
+This system keeps permission logic in one place so we can:
+
+- update permission behavior globally
+- reuse hooks and boundary components
+- keep feature components readable
+
+---
+
+## High-Level Architecture
+
+The permission system lives in `src/permissions/`.
+
+### Main files
+
+- `src/permissions/constants.ts`
+ Contains permission keys such as `event:create` and `member:view`.
+
+- `src/permissions/permission.service.ts`
+ Fetches permissions for the current user and defines query keys.
+
+- `src/permissions/PermissionBootstrap.tsx`
+ Runs once near app startup, loads permissions through React Query, and syncs them into Redux.
+
+- `src/store/permissionsSlice.ts`
+ Stores granted permissions plus loading, error, and sync metadata.
+
+- `src/permissions/useAuthorization.tsx`
+ Exposes reusable hooks for permission checks:
+ `useAuthorization`, `usePermissionMap`, and `PermissionGate`.
+
+- `src/permissions/PermissionBoundary.tsx`
+ Handles page-level permission states:
+ loading, allowed, unauthorized.
+
+- `src/permissions/PermissionLoading.tsx`
+ Provides a clean loading state while access is being validated.
+
+- `src/permissions/AccessDenied.tsx`
+ Provides a reusable unauthorized state.
+
+- `src/permissions/selectors.ts`
+ Central Redux selectors for permission state.
+
+- `src/permissions/utils.ts`
+ Shared helpers for normalizing permission input and evaluating access.
+
+---
+
+## Data Flow
+
+### Step 1: App starts
+
+`PermissionBootstrap` mounts near the app root in `src/main.tsx`.
+
+### Step 2: Permissions are fetched
+
+The bootstrap component uses `TanStack Query` to fetch permissions for the current user role.
+
+### Step 3: Permissions are cached
+
+React Query caches the result and prevents unnecessary refetching during the cache window.
+
+### Step 4: Permissions are stored globally
+
+Once fetched, permissions are written into Redux so any component can read them quickly without re-implementing fetch logic.
+
+### Step 5: Components consume permissions
+
+Components use:
+
+- `PermissionBoundary` for full-page access control
+- `PermissionGate` for showing or hiding small UI sections
+- `usePermissionMap` for components with multiple action buttons
+- `useAuthorization` for lower-level custom logic
+
+---
+
+## Permission Constants
+
+The system uses named constants instead of hard-coded strings.
+
+Example:
+
+```ts
+export const Event_Permissions = {
+ CREATE_EVENT: "event:create",
+ UPDATE_EVENT: "event:update",
+ DELETE_EVENT: "event:delete",
+ VIEW_EVENT: "event:view",
+ PUBLISH_EVENT: "event:publish",
+ JOIN_EVENT: "event:join",
+ LEAVE_EVENT: "event:leave",
+} as const;
+```
+
+Why this matters:
+
+- avoids typos
+- improves autocomplete
+- makes refactoring safer
+- keeps permission usage consistent across modules
+
+---
+
+## When to Use Each API
+
+### `PermissionBoundary`
+
+Use for page-level or section-level protection when you need:
+
+- a loading state
+- an unauthorized fallback
+- a protected content area
+
+Example:
+
+```tsx
+
| Team Member | Role / Department | Email Address | -Actions | +{canEmail ? "Actions" : "Quick Copy"} | @@ -148,21 +153,24 @@ const InternalSupport_Table = () => {
-
- ((e.currentTarget as HTMLAnchorElement).style.backgroundColor =
- "var(--cd-primary-subtle)")
- }
- onMouseLeave={(e) =>
- ((e.currentTarget as HTMLAnchorElement).style.backgroundColor = "transparent")
- }
- >
-
|
diff --git a/src/features/Contact_And_Support/v1/Components/Support.tsx b/src/features/Contact_And_Support/v1/Components/Support.tsx
index cb237bf..dce6506 100644
--- a/src/features/Contact_And_Support/v1/Components/Support.tsx
+++ b/src/features/Contact_And_Support/v1/Components/Support.tsx
@@ -1,5 +1,6 @@
import DropDown from "@/Component/ui/DropDown";
import Input from "@/Component/ui/Input";
+import { Contact_Permissions, usePermissionMap } from "@/permissions";
import { FormEvent, useState } from "react";
import { MdOutlineSupportAgent } from "react-icons/md";
import { FiAlertCircle, FiCheckCircle } from "react-icons/fi";
@@ -22,6 +23,9 @@ const priorityColor: RecordTeams | Submissions | -Actions | + {canManageActions &&Actions | } @@ -74,21 +81,26 @@ function EventTable({ events, itemsPerPage }: EventProps) {{event.teams} | {event.submissions} | -
- |
+ {canManageActions && (
+
+
+ {canView && (
+
+ |
+ )}
);
})}
diff --git a/src/features/Events/v1/Components/Judge.tsx b/src/features/Events/v1/Components/Judge.tsx
index c827e26..87ff92b 100644
--- a/src/features/Events/v1/Components/Judge.tsx
+++ b/src/features/Events/v1/Components/Judge.tsx
@@ -1,5 +1,6 @@
import { useMemo, useState } from "react";
import Input from "@/Component/ui/Input";
+import { Event_Permissions, PermissionGate } from "@/permissions";
import { CiSearch } from "react-icons/ci";
import { IoMdAdd } from "react-icons/io";
import JudgeCard from "./JudgeCard";
@@ -53,9 +54,11 @@ const Judge = ({ isExpanded = true, onToggleExpand }: JudgeProps) => {
Status | Skills | Certificates | -Actions | + {canManageMembers &&Actions | } @@ -55,21 +61,18 @@ const MemberTable = ({ members }: MemberTableProps) => {
- |
+ {canManageMembers && (
+
+
+ {canEdit && (
+
+ |
+ )}
))}
diff --git a/src/features/Member/v1/Pages/MemberPage.tsx b/src/features/Member/v1/Pages/MemberPage.tsx
index 8ba6956..33c165c 100644
--- a/src/features/Member/v1/Pages/MemberPage.tsx
+++ b/src/features/Member/v1/Pages/MemberPage.tsx
@@ -1,4 +1,5 @@
import MemberHeader from "../Components/MemberHeader";
+import { AccessDenied, Member_Permissions, PermissionBoundary, PermissionLoading } from "@/permissions";
import MemberTable from "../Components/MemberTable";
import SearchMember from "../Components/SearchMember";
@@ -41,8 +42,19 @@ const MemberPage = () => {
return (
|---|