diff --git a/backend/package.json b/backend/package.json
index 41935d2..9acefd6 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -1,6 +1,6 @@
{
"name": "jama-backend",
- "version": "0.10.7",
+ "version": "0.10.8",
"description": "TeamChat backend server",
"main": "src/index.js",
"scripts": {
diff --git a/build.sh b/build.sh
index 8d530ed..8c1abeb 100644
--- a/build.sh
+++ b/build.sh
@@ -13,7 +13,7 @@
# ─────────────────────────────────────────────────────────────
set -euo pipefail
-VERSION="${1:-0.10.7}"
+VERSION="${1:-0.10.8}"
ACTION="${2:-}"
REGISTRY="${REGISTRY:-}"
IMAGE_NAME="jama"
diff --git a/frontend/package.json b/frontend/package.json
index 54754e2..a69452d 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "jama-frontend",
- "version": "0.10.7",
+ "version": "0.10.8",
"private": true,
"scripts": {
"dev": "vite",
diff --git a/frontend/src/pages/GroupManagerPage.jsx b/frontend/src/pages/GroupManagerPage.jsx
index bca0bc1..50ccd79 100644
--- a/frontend/src/pages/GroupManagerPage.jsx
+++ b/frontend/src/pages/GroupManagerPage.jsx
@@ -495,6 +495,8 @@ function U2URestrictionsTab({ allUserGroups, isMobile = false }) {
}
// ── Main page ─────────────────────────────────────────────────────────────────
+const SIDEBAR_W = 260;
+
export default function GroupManagerPage({ isMobile = false, onProfile, onHelp, onAbout }) {
const [tab, setTab] = useState('all');
const [allUsers, setAllUsers] = useState([]);
@@ -508,31 +510,47 @@ export default function GroupManagerPage({ isMobile = false, onProfile, onHelp,
}, [refreshKey]);
return (
-
- {/* Page header */}
-
-
-
-
-
-
-
+
+
+ {/* ── Left panel (desktop only) ── */}
+ {!isMobile && (
+
+ )}
+
+ {/* ── Right panel ── */}
+
+
+ {/* Header */}
+
+
+
+
+
+
+
+
-
- {/* Content */}
-
- {tab==='all' &&
}
- {tab==='dm' &&
}
- {tab==='u2u' &&
}
-
- {/* User footer */}
-
-
+ {/* Content */}
+
+ {tab==='all' &&
}
+ {tab==='dm' &&
}
+ {tab==='u2u' &&
}
+
+
+ {/* Mobile footer — fixed at bottom, always visible */}
+ {isMobile && (
+
+
+
+ )}
);
diff --git a/frontend/src/pages/UserManagerPage.jsx b/frontend/src/pages/UserManagerPage.jsx
index e8f2c80..9ecef3a 100644
--- a/frontend/src/pages/UserManagerPage.jsx
+++ b/frontend/src/pages/UserManagerPage.jsx
@@ -4,6 +4,8 @@ import { api } from '../utils/api.js';
import Avatar from '../components/Avatar.jsx';
import UserFooter from '../components/UserFooter.jsx';
+const SIDEBAR_W = 260;
+
function isValidEmail(e) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e); }
function parseCSV(text) {
@@ -118,7 +120,6 @@ function UserRow({ u, onUpdated }) {
Edit Name
)}
-
{roleWarning && Role Required}
-
{showReset ? (
)}
-
{u.status==='active' ? (
@@ -162,7 +161,7 @@ function UserRow({ u, onUpdated }) {
}
// ── Create user form ──────────────────────────────────────────────────────────
-function CreateUserForm({ userPass, onCreated, isMobile = false }) {
+function CreateUserForm({ userPass, onCreated, isMobile }) {
const toast = useToast();
const [form, setForm] = useState({ name:'', email:'', password:'', role:'member' });
const [saving, setSaving] = useState(false);
@@ -183,7 +182,7 @@ function CreateUserForm({ userPass, onCreated, isMobile = false }) {
};
return (
-
+
@@ -256,11 +255,7 @@ function BulkImportForm({ userPass, onCreated }) {
{csvFile && {csvFile.name}{csvRows.length > 0 && ({csvRows.length} valid)}}
- {csvRows.length > 0 && (
-
- )}
+ {csvRows.length > 0 && }
{csvInvalid.length > 0 && (
@@ -300,6 +295,7 @@ export default function UserManagerPage({ isMobile = false, onProfile, onHelp, o
const [search, setSearch] = useState('');
const [tab, setTab] = useState('users');
const [userPass, setUserPass] = useState('user@1234');
+ const [inputFocused, setInputFocused] = useState(false);
const load = async () => {
setLoadError(''); setLoading(true);
@@ -321,57 +317,72 @@ export default function UserManagerPage({ isMobile = false, onProfile, onHelp, o
u.email?.toLowerCase().includes(search.toLowerCase())
);
- const pad = isMobile ? 16 : 24;
return (
-
- {/* Page header */}
-
-
-
-
-
User Manager
- {!loading &&
{users.length} user{users.length!==1?'s':''}}
-
-
-
-
- {!isMobile &&
}
+
+
+ {/* ── Left panel (desktop only) — blank, reserved for future use ── */}
+ {!isMobile && (
+
+ )}
+
+ {/* ── Right panel ── */}
+
+
+ {/* Header */}
+
+
+
+
+
User Manager
+ {!loading &&
{users.length} user{users.length!==1?'s':''}}
+
+
+
+
+ {!isMobile && }
+
-
- {/* Content */}
-
- {tab === 'users' && (
- <>
-
setSearch(e.target.value)}
- autoComplete="new-password" autoCorrect="off" spellCheck={false}
- style={{ marginBottom:16, width:'100%', maxWidth: isMobile ? '100%' : 400 }} />
-
- {loading ? (
-
- ) : loadError ? (
-
-
⚠ {loadError}
-
-
- ) : filtered.length === 0 ? (
-
- {search ? 'No users match your search.' : 'No users yet.'}
-
- ) : (
- filtered.map(u =>
)
- )}
-
- >
+ {/* Content */}
+
+ {tab === 'users' && (
+ <>
+
setSearch(e.target.value)}
+ onFocus={() => setInputFocused(true)} onBlur={() => setInputFocused(false)}
+ autoComplete="new-password" autoCorrect="off" spellCheck={false}
+ style={{ marginBottom:16, width:'100%', maxWidth: isMobile ? '100%' : 400 }} />
+
+ {loading ? (
+
+ ) : loadError ? (
+
+
⚠ {loadError}
+
+
+ ) : filtered.length === 0 ? (
+
+ {search ? 'No users match your search.' : 'No users yet.'}
+
+ ) : (
+ filtered.map(u =>
)
+ )}
+
+ >
+ )}
+ {tab === 'create' &&
{ load(); setTab('users'); }} isMobile={isMobile} />}
+ {tab === 'bulk' && }
+
+
+ {/* Mobile footer — fixed at bottom, hidden when keyboard open */}
+ {isMobile && !inputFocused && (
+
+
+
)}
- {tab === 'create' &&
{ load(); setTab('users'); }} isMobile={isMobile} />}
- {tab === 'bulk' && }
-
-
- {/* User footer */}
-
-
);