feat: add member removal functionality with confirmation and error handling
This commit is contained in:
parent
bc7ff54380
commit
0f4844abb8
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user