From b224237cf7c92c18508bb2822adaa6440ed9dc49 Mon Sep 17 00:00:00 2001 From: Ricky Stretch Date: Fri, 20 Mar 2026 21:34:53 -0400 Subject: [PATCH] v0.10.7 fixed UI settings --- backend/package.json | 2 +- build.sh | 2 +- frontend/package.json | 2 +- frontend/src/components/HostPanel.jsx | 8 ++- frontend/src/pages/Chat.jsx | 6 +-- frontend/src/pages/GroupManagerPage.jsx | 72 ++++++++++++++++--------- frontend/src/pages/UserManagerPage.jsx | 32 ++++++----- 7 files changed, 79 insertions(+), 45 deletions(-) diff --git a/backend/package.json b/backend/package.json index 74fb4db..41935d2 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "jama-backend", - "version": "0.10.6", + "version": "0.10.7", "description": "TeamChat backend server", "main": "src/index.js", "scripts": { diff --git a/build.sh b/build.sh index b674c93..8d530ed 100644 --- a/build.sh +++ b/build.sh @@ -13,7 +13,7 @@ # ───────────────────────────────────────────────────────────── set -euo pipefail -VERSION="${1:-0.10.6}" +VERSION="${1:-0.10.7}" ACTION="${2:-}" REGISTRY="${REGISTRY:-}" IMAGE_NAME="jama" diff --git a/frontend/package.json b/frontend/package.json index d9818cd..54754e2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "jama-frontend", - "version": "0.10.6", + "version": "0.10.7", "private": true, "scripts": { "dev": "vite", diff --git a/frontend/src/components/HostPanel.jsx b/frontend/src/components/HostPanel.jsx index fbd1e94..43407fe 100644 --- a/frontend/src/components/HostPanel.jsx +++ b/frontend/src/components/HostPanel.jsx @@ -10,6 +10,7 @@ import { useState, useEffect, useCallback } from 'react'; import { useAuth } from '../contexts/AuthContext.jsx'; +import UserFooter from './UserFooter.jsx'; // ── Constants ───────────────────────────────────────────────────────────────── @@ -335,7 +336,7 @@ function KeyEntry({ onSubmit }) { // ── Main HostPanel ──────────────────────────────────────────────────────────── -export default function HostPanel() { +export default function HostPanel({ onProfile, onHelp, onAbout }) { const { user } = useAuth(); const [adminKey, setAdminKey] = useState(() => sessionStorage.getItem('jama-host-key') || ''); const [status, setStatus] = useState(null); @@ -486,6 +487,11 @@ export default function HostPanel() { ))} + + {/* User footer */} +
+ +
); } diff --git a/frontend/src/pages/Chat.jsx b/frontend/src/pages/Chat.jsx index 5dea450..2a5e153 100644 --- a/frontend/src/pages/Chat.jsx +++ b/frontend/src/pages/Chat.jsx @@ -338,7 +338,7 @@ export default function Chat() {
setDrawerOpen(true)} />
- + setModal('profile')} onHelp={() => setModal('help')} onAbout={() => setModal('about')} />
setDrawerOpen(false)} @@ -362,7 +362,7 @@ export default function Chat() {
setDrawerOpen(true)} />
- + setModal('profile')} onHelp={() => setModal('help')} onAbout={() => setModal('about')} />
setDrawerOpen(false)} @@ -386,7 +386,7 @@ export default function Chat() {
setDrawerOpen(true)} />
- + setModal('profile')} onHelp={() => setModal('help')} onAbout={() => setModal('about')} />
+
{/* Sidebar list */} -
+
User Groups
}
-
-
+
+
setDmName(e.target.value)} placeholder="e.g. Coaches + Players" style={{ marginTop:6 }} autoComplete="new-password" /> @@ -268,7 +269,7 @@ function DirectMessagesTab({ allUserGroups, onRefresh, refreshKey }) { // ── U2U Restrictions tab ────────────────────────────────────────────────────── -function U2URestrictionsTab({ allUserGroups }) { +function U2URestrictionsTab({ allUserGroups, isMobile = false }) { const toast = useToast(); const [selectedGroup, setSelectedGroup] = useState(null); const [blockedIds, setBlockedIds] = useState(new Set()); @@ -276,6 +277,22 @@ function U2URestrictionsTab({ allUserGroups }) { const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(false); const [search, setSearch] = useState(''); + // Map of groupId → number of restrictions (for showing dots in sidebar) + const [restrictionCounts, setRestrictionCounts] = useState({}); + + // Load restriction counts for all groups on mount and after saves + const loadAllCounts = useCallback(async () => { + const counts = {}; + for (const g of allUserGroups) { + try { + const { blockedGroupIds } = await api.getGroupRestrictions(g.id); + counts[g.id] = blockedGroupIds.length; + } catch { counts[g.id] = 0; } + } + setRestrictionCounts(counts); + }, [allUserGroups]); + + useEffect(() => { if (allUserGroups.length > 0) loadAllCounts(); }, [allUserGroups]); const loadRestrictions = async (group) => { setLoading(true); @@ -314,6 +331,7 @@ function U2URestrictionsTab({ allUserGroups }) { await api.setGroupRestrictions(selectedGroup.id, [...blockedIds]); setSavedBlockedIds(new Set(blockedIds)); toast('Restrictions saved', 'success'); + loadAllCounts(); } catch (e) { toast(e.message, 'error'); } finally { setSaving(false); } }; @@ -328,14 +346,14 @@ function U2URestrictionsTab({ allUserGroups }) { : otherGroups; return ( -
+
{/* Group selector sidebar */} -
+
Select Group
{allUserGroups.map(g => { - const hasRestrictions = g.id === selectedGroup?.id ? blockedIds.size > 0 : false; + const hasRestrictions = g.id === selectedGroup?.id ? blockedIds.size > 0 : (restrictionCounts[g.id] || 0) > 0; return (
{hasRestrictions && ( - + )}
@@ -363,7 +381,7 @@ function U2URestrictionsTab({ allUserGroups }) {
{/* Restriction editor */} -
+
{!selectedGroup ? (
@@ -376,7 +394,7 @@ function U2URestrictionsTab({ allUserGroups }) {
) : ( -
+
{/* Header */}

{selectedGroup.name}

@@ -477,7 +495,7 @@ function U2URestrictionsTab({ allUserGroups }) { } // ── Main page ───────────────────────────────────────────────────────────────── -export default function GroupManagerPage() { +export default function GroupManagerPage({ isMobile = false, onProfile, onHelp, onAbout }) { const [tab, setTab] = useState('all'); const [allUsers, setAllUsers] = useState([]); const [allUserGroups, setAllUserGroups] = useState([]); @@ -499,18 +517,22 @@ export default function GroupManagerPage() { Group Manager
- - - + + +
{/* Content */} -
- {tab==='all' && } - {tab==='dm' && } - {tab==='u2u' && } +
+ {tab==='all' && } + {tab==='dm' && } + {tab==='u2u' && } +
+ {/* User footer */} +
+
); diff --git a/frontend/src/pages/UserManagerPage.jsx b/frontend/src/pages/UserManagerPage.jsx index 5a1c04b..e8f2c80 100644 --- a/frontend/src/pages/UserManagerPage.jsx +++ b/frontend/src/pages/UserManagerPage.jsx @@ -2,6 +2,7 @@ import { useState, useEffect, useRef } from 'react'; import { useToast } from '../contexts/ToastContext.jsx'; import { api } from '../utils/api.js'; import Avatar from '../components/Avatar.jsx'; +import UserFooter from '../components/UserFooter.jsx'; function isValidEmail(e) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e); } @@ -161,7 +162,7 @@ function UserRow({ u, onUpdated }) { } // ── Create user form ────────────────────────────────────────────────────────── -function CreateUserForm({ userPass, onCreated }) { +function CreateUserForm({ userPass, onCreated, isMobile = false }) { const toast = useToast(); const [form, setForm] = useState({ name:'', email:'', password:'', role:'member' }); const [saving, setSaving] = useState(false); @@ -182,8 +183,8 @@ function CreateUserForm({ userPass, onCreated }) { }; return ( -
-
+
+
set('name')(e.target.value)} /> @@ -292,8 +293,7 @@ function BulkImportForm({ userPass, onCreated }) { } // ── Main page ───────────────────────────────────────────────────────────────── -export default function UserManagerPage() { - const isMobile = window.innerWidth < 768; +export default function UserManagerPage({ isMobile = false, onProfile, onHelp, onAbout }) { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [loadError, setLoadError] = useState(''); @@ -321,31 +321,32 @@ export default function UserManagerPage() { u.email?.toLowerCase().includes(search.toLowerCase()) ); + const pad = isMobile ? 16 : 24; return (
{/* Page header */} -
-
+
+
User Manager - {!loading && {users.length} user{users.length!==1?'s':''}} + {!loading && {users.length} user{users.length!==1?'s':''}}
- + - {!isMobile && } + {!isMobile && }
{/* Content */} -
+
{tab === 'users' && ( <> setSearch(e.target.value)} autoComplete="new-password" autoCorrect="off" spellCheck={false} - style={{ marginBottom:16, maxWidth:400 }} /> + style={{ marginBottom:16, width:'100%', maxWidth: isMobile ? '100%' : 400 }} />
{loading ? (
@@ -364,9 +365,14 @@ export default function UserManagerPage() {
)} - {tab === 'create' && { load(); setTab('users'); }} />} + {tab === 'create' && { load(); setTab('users'); }} isMobile={isMobile} />} {tab === 'bulk' && }
+ + {/* User footer */} +
+ +
); }