feat: add member removal functionality with confirmation and error handling

This commit is contained in:
seaznCode 2026-01-28 20:15:17 +01:00
parent bc7ff54380
commit 0f4844abb8
2 changed files with 38 additions and 1 deletions

View File

@ -73,6 +73,8 @@ function PoolManagePageInner() {
const [hasSearched, setHasSearched] = React.useState(false)
const [selectedCandidates, setSelectedCandidates] = React.useState<Set<string>>(new Set())
const [savingMembers, setSavingMembers] = React.useState(false)
const [removingMemberId, setRemovingMemberId] = React.useState<string | null>(null)
const [removeError, setRemoveError] = React.useState<string>('')
async function fetchMembers() {
if (!token || !poolId || poolId === 'pool-unknown') return
@ -211,6 +213,23 @@ function PoolManagePageInner() {
}
}
async function removeMember(userId: string) {
if (!token || !poolId || poolId === 'pool-unknown') return
const user = users.find(u => u.id === userId)
const label = user?.name || user?.email || 'this user'
if (!window.confirm(`Remove ${label} from this pool?`)) return
setRemoveError('')
setRemovingMemberId(userId)
try {
await AdminAPI.removePoolMembers(token, poolId, [userId])
await fetchMembers()
} catch (e: any) {
setRemoveError(e?.message || 'Failed to remove user from pool.')
} finally {
setRemovingMemberId(null)
}
}
return (
<PageTransitionEffect>
<div className="min-h-screen flex flex-col bg-gradient-to-tr from-blue-50 via-white to-blue-100">
@ -302,6 +321,11 @@ function PoolManagePageInner() {
Add User
</button>
</div>
{removeError && (
<div className="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700">
{removeError}
</div>
)}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{users.map(u => (
<article key={u.id} className="rounded-2xl bg-white border border-gray-100 shadow p-5 flex flex-col">
@ -325,6 +349,15 @@ function PoolManagePageInner() {
{new Date(u.joinedAt).toLocaleDateString('de-DE', { year: 'numeric', month: 'short', day: '2-digit' })}
</span>
</div>
<div className="mt-4 flex justify-end">
<button
onClick={() => removeMember(u.id)}
disabled={removingMemberId === u.id}
className="px-3 py-2 text-xs font-medium rounded-lg border border-red-200 bg-red-50 text-red-700 hover:bg-red-100 transition disabled:opacity-60 disabled:cursor-not-allowed"
>
{removingMemberId === u.id ? 'Removing…' : 'Remove'}
</button>
</div>
</article>
))}
{membersLoading && (
@ -392,7 +425,7 @@ function PoolManagePageInner() {
</button>
<button
type="button"
onClick={() => { setQuery(''); setCandidates([]); setHasSearched(false); setError(''); }}
onClick={() => { setQuery(''); setError(''); }}
className="rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 transition"
>
Clear

View File

@ -81,6 +81,8 @@ export default function PoolManagementPage() {
}
async function handleArchive(poolId: string) {
const confirmed = window.confirm('Archive this pool? Users will no longer be able to join or use it.')
if (!confirmed) return
setArchiveError('')
const res = await setPoolInactive(poolId)
if (res.ok) {
@ -91,6 +93,8 @@ export default function PoolManagementPage() {
}
async function handleSetActive(poolId: string) {
const confirmed = window.confirm('Unarchive this pool and make it active again?')
if (!confirmed) return
setArchiveError('')
const res = await setPoolActive(poolId)
if (res.ok) {