diff --git a/backend/package.json b/backend/package.json
index 1df4df5..a7e4f7a 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -1,6 +1,6 @@
{
"name": "rosterchirp-backend",
- "version": "0.12.9",
+ "version": "0.12.10",
"description": "RosterChirp backend server",
"main": "src/index.js",
"scripts": {
diff --git a/build.sh b/build.sh
index 4ff347c..4f58bd5 100644
--- a/build.sh
+++ b/build.sh
@@ -13,7 +13,7 @@
# ─────────────────────────────────────────────────────────────
set -euo pipefail
-VERSION="${1:-0.12.9}"
+VERSION="${1:-0.12.10}"
ACTION="${2:-}"
REGISTRY="${REGISTRY:-}"
IMAGE_NAME="rosterchirp"
diff --git a/frontend/package.json b/frontend/package.json
index 7a5eb47..18528db 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "rosterchirp-frontend",
- "version": "0.12.9",
+ "version": "0.12.10",
"private": true,
"scripts": {
"dev": "vite",
diff --git a/frontend/src/pages/GroupManagerPage.jsx b/frontend/src/pages/GroupManagerPage.jsx
index 9b534b9..b9a4cc4 100644
--- a/frontend/src/pages/GroupManagerPage.jsx
+++ b/frontend/src/pages/GroupManagerPage.jsx
@@ -21,7 +21,9 @@ import Avatar from '../components/Avatar.jsx';
function UserCheckList({ allUsers, selectedIds, onChange, onIF, onIB }) {
const [search, setSearch] = useState('');
- const filtered = allUsers.filter(u => (u.display_name||u.name).toLowerCase().includes(search.toLowerCase()));
+ const filtered = allUsers
+ .filter(u => (u.display_name||u.name).toLowerCase().includes(search.toLowerCase()))
+ .sort((a, b) => (a.display_name||a.name).localeCompare(b.display_name||b.name));
return (
setSearch(e.target.value)} autoComplete="new-password" style={{ marginBottom:8 }} autoComplete="new-password" onFocus={onIF} onBlur={onIB} />
diff --git a/frontend/src/pages/UserManagerPage.jsx b/frontend/src/pages/UserManagerPage.jsx
index 9e8abc0..42824ba 100644
--- a/frontend/src/pages/UserManagerPage.jsx
+++ b/frontend/src/pages/UserManagerPage.jsx
@@ -356,33 +356,47 @@ export default function UserManagerPage({ isMobile = false, onProfile, onHelp, o
)}
{/* Content */}
-
+
{tab === 'users' && (
<>
-
setSearch(e.target.value)}
- onFocus={onIF} onBlur={onIB}
- 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 =>
)
- )}
+ {/* Search — always visible, outside scroll area */}
+
+ setSearch(e.target.value)}
+ onFocus={onIF} onBlur={onIB}
+ autoComplete="new-password" autoCorrect="off" spellCheck={false}
+ style={{ width:'100%', maxWidth: isMobile ? '100%' : 400 }} />
+
+ {/* User list — bounded scroll */}
+
+
+ {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} onIF={onIF} onIB={onIB} />}
- {tab === 'bulk' && }
+ {tab === 'create' && (
+
+ { load(); setTab('users'); }} isMobile={isMobile} onIF={onIF} onIB={onIB} />
+
+ )}
+ {tab === 'bulk' && (
+
+
+
+ )}
{/* Mobile footer — fixed, hidden when any input is focused (keyboard open) */}