From 44799f76cc13f8c9442011e76eaaf7889b4d3430 Mon Sep 17 00:00:00 2001 From: Ricky Stretch Date: Tue, 24 Mar 2026 07:57:03 -0400 Subject: [PATCH] v0.12.13 user manager permissions + member event calendar --- backend/package.json | 2 +- backend/src/middleware/auth.js | 2 +- backend/src/routes/schedule.js | 2 +- build.sh | 2 +- frontend/package.json | 2 +- frontend/src/components/SchedulePage.jsx | 5 +++-- frontend/src/pages/Chat.jsx | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/backend/package.json b/backend/package.json index be224db..25f9616 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "rosterchirp-backend", - "version": "0.12.12", + "version": "0.12.13", "description": "RosterChirp backend server", "main": "src/index.js", "scripts": { diff --git a/backend/src/middleware/auth.js b/backend/src/middleware/auth.js index ef8ff76..c762d80 100644 --- a/backend/src/middleware/auth.js +++ b/backend/src/middleware/auth.js @@ -39,7 +39,7 @@ function adminMiddleware(req, res, next) { } async function teamManagerMiddleware(req, res, next) { - if (req.user?.role === 'admin') return next(); + if (req.user?.role === 'admin' || req.user?.role === 'manager') return next(); try { const tmSetting = await queryOne(req.schema, "SELECT value FROM settings WHERE key = 'team_tool_managers'" diff --git a/backend/src/routes/schedule.js b/backend/src/routes/schedule.js index 0d52461..0b8c924 100644 --- a/backend/src/routes/schedule.js +++ b/backend/src/routes/schedule.js @@ -54,7 +54,7 @@ async function postEventNotification(schema, eventId, actorId, isUpdate) { // ── Helpers ─────────────────────────────────────────────────────────────────── async function isToolManagerFn(schema, user) { - if (user.role === 'admin') return true; + if (user.role === 'admin' || user.role === 'manager') return true; const tm = await queryOne(schema, "SELECT value FROM settings WHERE key='team_tool_managers'"); const gm = await queryOne(schema, "SELECT value FROM settings WHERE key='team_group_managers'"); const groupIds = [...new Set([...JSON.parse(tm?.value||'[]'), ...JSON.parse(gm?.value||'[]')])]; diff --git a/build.sh b/build.sh index a900a30..b365560 100644 --- a/build.sh +++ b/build.sh @@ -13,7 +13,7 @@ # ───────────────────────────────────────────────────────────── set -euo pipefail -VERSION="${1:-0.12.12}" +VERSION="${1:-0.12.13}" ACTION="${2:-}" REGISTRY="${REGISTRY:-}" IMAGE_NAME="rosterchirp" diff --git a/frontend/package.json b/frontend/package.json index 6f6cba7..ac91cbf 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "rosterchirp-frontend", - "version": "0.12.12", + "version": "0.12.13", "private": true, "scripts": { "dev": "vite", diff --git a/frontend/src/components/SchedulePage.jsx b/frontend/src/components/SchedulePage.jsx index 45275fe..d6970cd 100644 --- a/frontend/src/components/SchedulePage.jsx +++ b/frontend/src/components/SchedulePage.jsx @@ -1364,10 +1364,11 @@ export default function SchedulePage({ isToolManager, isMobile, onProfile, onHel const createRef = useRef(null); const load = useCallback(() => { - Promise.all([api.getEvents(), api.getEventTypes(), api.getUserGroups()]) + const ugPromise = isToolManager ? api.getUserGroups() : Promise.resolve({ groups: [] }); + Promise.all([api.getEvents(), api.getEventTypes(), ugPromise]) .then(([ev,et,ug]) => { setEvents(ev.events||[]); setEventTypes(et.eventTypes||[]); setUserGroups(ug.groups||[]); setLoading(false); }) .catch(() => setLoading(false)); - }, []); + }, [isToolManager]); useEffect(() => { load(); }, [load]); diff --git a/frontend/src/pages/Chat.jsx b/frontend/src/pages/Chat.jsx index 0772de0..e23edc9 100644 --- a/frontend/src/pages/Chat.jsx +++ b/frontend/src/pages/Chat.jsx @@ -393,7 +393,7 @@ export default function Chat() { ...(groups.privateGroups || []) ].find(g => g.id === activeGroupId); - const isToolManager = user?.role === 'admin' || (features.teamToolManagers || []).some(gid => (features.userGroupMemberships || []).includes(gid)); + const isToolManager = user?.role === 'admin' || user?.role === 'manager' || (features.teamToolManagers || []).some(gid => (features.userGroupMemberships || []).includes(gid)); // Unread indicators for burger icon and nav drawer const allGroupsFlat = [...(groups.publicGroups || []), ...(groups.privateGroups || [])];