From b007b9cf1f1d31ee32cdd2c223de276e97a1ec91 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 27 Jan 2026 18:55:23 +0000
Subject: [PATCH 1/7] Initial plan
From faeb0cc5ce43244374da83ee935223e7b452c4ed Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 27 Jan 2026 18:59:25 +0000
Subject: [PATCH 2/7] feat: implement role-based UI/UX with actual user role
enforcement
Co-authored-by: Olu1111 <164207207+Olu1111@users.noreply.github.com>
---
frontend/src/components/Navbar.jsx | 21 +++++++-
frontend/src/pages/BoardViewPage.jsx | 39 ++++++++-------
frontend/src/pages/BoardsList.jsx | 11 +++--
frontend/src/utils/auth.js | 71 ++++++++++++++++++++++++++++
4 files changed, 120 insertions(+), 22 deletions(-)
create mode 100644 frontend/src/utils/auth.js
diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx
index 3be99c1..93f5cc2 100644
--- a/frontend/src/components/Navbar.jsx
+++ b/frontend/src/components/Navbar.jsx
@@ -1,10 +1,11 @@
import React, { useState, useEffect, useCallback, useRef } from 'react';
-import { AppBar, Toolbar, Typography, Button, Box, IconButton, Badge, Menu, Divider } from '@mui/material';
+import { AppBar, Toolbar, Typography, Button, Box, IconButton, Badge, Menu, Divider, Chip } from '@mui/material';
import NotificationsIcon from '@mui/icons-material/Notifications';
import { useNavigate, useLocation } from 'react-router-dom';
import GlobalSearch from './GlobalSearch';
import NotificationItem from './NotificationItem';
import { apiClient } from '../utils/apiClient';
+import { getUserRole } from '../utils/auth';
export default function Navbar({ authenticated }) {
const navigate = useNavigate();
@@ -17,6 +18,9 @@ export default function Navbar({ authenticated }) {
const pathParts = location.pathname.split('/');
const boardId = pathParts[1] === 'boards' && pathParts[2] ? pathParts[2] : null;
+ // Get user role for display
+ const userRole = getUserRole();
+
const fetchNotifications = useCallback(async () => {
if (authenticated !== true || !boardId) {
if (notifications.length > 0) setNotifications([]);
@@ -110,6 +114,21 @@ export default function Navbar({ authenticated }) {
{authenticated === true && (
+ {userRole && (
+
+ )}
+
setAnchorEl(e.currentTarget)}>
diff --git a/frontend/src/pages/BoardViewPage.jsx b/frontend/src/pages/BoardViewPage.jsx
index 385f5c4..d72d5b3 100644
--- a/frontend/src/pages/BoardViewPage.jsx
+++ b/frontend/src/pages/BoardViewPage.jsx
@@ -13,6 +13,7 @@ import CloseIcon from '@mui/icons-material/Close';
import AddIcon from '@mui/icons-material/Add';
import { useNavigate, useParams } from 'react-router-dom';
import { apiClient } from '../utils/apiClient';
+import { isAdmin, isMember } from '../utils/auth';
import BoardSkeleton from '../components/BoardSkeleton';
import ConfirmDeleteModal from '../components/ConfirmDeleteModal';
import TicketModal from '../components/TicketModal';
@@ -84,7 +85,9 @@ const BoardViewPage = () => {
const [isColumnModalOpen, setIsColumnModalOpen] = useState(false);
const [snackbar, setSnackbar] = useState({ open: false, message: '', severity: 'success' });
- const isAdmin = true;
+ // Check user role from localStorage
+ const userIsAdmin = isAdmin();
+ const userIsMember = isMember();
const triggerNotificationSync = useCallback(() => {
window.dispatchEvent(new Event('refreshNotifications'));
@@ -267,20 +270,22 @@ const BoardViewPage = () => {
{/* COLOR FIX: Using Charcoal Grey #263238 */}
- }
- onClick={() => setIsColumnModalOpen(true)}
- sx={{
- borderRadius: '8px',
- textTransform: 'none',
- fontWeight: 700,
- bgcolor: '#263238',
- '&:hover': { bgcolor: '#37474f' }
- }}
- >
- Add Column
-
+ {userIsMember && (
+ }
+ onClick={() => setIsColumnModalOpen(true)}
+ sx={{
+ borderRadius: '8px',
+ textTransform: 'none',
+ fontWeight: 700,
+ bgcolor: '#263238',
+ '&:hover': { bgcolor: '#37474f' }
+ }}
+ >
+ Add Column
+
+ )}
{columns.length > 0 && setFilters(initialFilters)} />}
@@ -293,7 +298,7 @@ const BoardViewPage = () => {
handleRenameColumn(column._id, e.target.value)} style={{ background: 'transparent', border: 'none', fontWeight: 'inherit', outline: 'none', width: '100%', fontFamily: 'inherit', fontSize: 'inherit' }} />
- {isAdmin && { setColumnToDelete(column); setIsDeleteModalOpen(true); }}>}
+ {userIsAdmin && { setColumnToDelete(column); setIsDeleteModalOpen(true); }}>}
@@ -329,7 +334,7 @@ const BoardViewPage = () => {
)}
- } onClick={() => { setActiveColumn(column); setIsTicketModalOpen(true); }} sx={{ textTransform: 'none', mt: 1, color: '#5e6c84', fontWeight: 600, justifyContent: 'flex-start' }}>Add a card
+ } onClick={() => { setActiveColumn(column); setIsTicketModalOpen(true); }} sx={{ textTransform: 'none', mt: 1, color: '#5e6c84', fontWeight: 600, justifyContent: 'flex-start', display: userIsMember ? 'flex' : 'none' }}>Add a card
))}
diff --git a/frontend/src/pages/BoardsList.jsx b/frontend/src/pages/BoardsList.jsx
index 68b24c4..9f1c888 100644
--- a/frontend/src/pages/BoardsList.jsx
+++ b/frontend/src/pages/BoardsList.jsx
@@ -5,7 +5,8 @@ import {
} from '@mui/material';
import DeleteIcon from '@mui/icons-material/Delete';
import { useNavigate } from 'react-router-dom';
-import BoardModal from '../components/BoardModal';
+import BoardModal from '../components/BoardModal';
+import { isAdmin, isMember } from '../utils/auth';
const BoardsList = () => {
const navigate = useNavigate();
@@ -14,7 +15,9 @@ const BoardsList = () => {
const [error, setError] = useState(null);
const [isModalOpen, setIsModalOpen] = useState(false);
- const isAdmin = true; // For visibility of admin controls
+ // Check user role from localStorage
+ const userIsAdmin = isAdmin();
+ const userIsMember = isMember();
const fetchBoards = async () => {
try {
@@ -74,7 +77,7 @@ const BoardsList = () => {
My Boards
Select a project to start working.
- {isAdmin && (
+ {userIsMember && (