From ebe123ed7edf24fbc7b8765ed709455a8513d6d5 Mon Sep 17 00:00:00 2001
From: Thomas Hallock
Date: Sun, 19 Oct 2025 12:07:14 -0500
Subject: [PATCH] fix: replace native alerts with inline confirmations in
ModerationPanel
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Removed native browser confirm() dialogs and replaced with React state-based inline confirmations:
- Removed confirm() from handleKick (kicks happen immediately)
- Removed confirm() from handleTransferOwnership
- Added confirmingTransferOwnership state variable
- Added inline confirmation UI with Cancel/Confirm buttons
- Follows pattern documented in UI_STYLE_GUIDE.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude
---
apps/web/.claude/settings.local.json | 3 +-
.../src/components/nav/ModerationPanel.tsx | 201 +++++++++++++-----
2 files changed, 144 insertions(+), 60 deletions(-)
diff --git a/apps/web/.claude/settings.local.json b/apps/web/.claude/settings.local.json
index ab4cf5c0..cf94f119 100644
--- a/apps/web/.claude/settings.local.json
+++ b/apps/web/.claude/settings.local.json
@@ -101,7 +101,8 @@
"WebFetch(domain:abaci.one)",
"Bash(do gh run list --limit 1 --workflow=\"Build and Deploy\" --json conclusion,status,databaseId --jq '.[0] | \"\"\\(.status) - \\(.conclusion // \"\"running\"\") - Run ID: \\(.databaseId)\"\"')",
"Bash(node -e:*)",
- "Bash(do gh run list --limit 1 --workflow=\"Build and Deploy\" --json conclusion,status,databaseId --jq '.[0] | \"\"\\(.status) - \\(.conclusion // \"\"running\"\") - Run \\(.databaseId)\"\"')"
+ "Bash(do gh run list --limit 1 --workflow=\"Build and Deploy\" --json conclusion,status,databaseId --jq '.[0] | \"\"\\(.status) - \\(.conclusion // \"\"running\"\") - Run \\(.databaseId)\"\"')",
+ "Bash(do ssh nas.home.network '/usr/local/bin/docker inspect soroban-abacus-flashcards --format=\"\"{{index .Config.Labels \\\"\"org.opencontainers.image.revision\\\"\"}}\"\"')"
],
"deny": [],
"ask": []
diff --git a/apps/web/src/components/nav/ModerationPanel.tsx b/apps/web/src/components/nav/ModerationPanel.tsx
index 9ba26498..faf5bb42 100644
--- a/apps/web/src/components/nav/ModerationPanel.tsx
+++ b/apps/web/src/components/nav/ModerationPanel.tsx
@@ -114,6 +114,9 @@ export function ModerationPanel({
null
)
+ // Transfer ownership confirmation state
+ const [confirmingTransferOwnership, setConfirmingTransferOwnership] = useState(false)
+
// Auto-switch to Members tab when focusedUserId is provided
useEffect(() => {
if (isOpen && focusedUserId) {
@@ -171,8 +174,6 @@ export function ModerationPanel({
}, [isOpen, roomId, members])
const handleKick = async (userId: string) => {
- if (!confirm('Kick this player from the room?')) return
-
setActionLoading(`kick-${userId}`)
try {
const res = await fetch(`/api/arcade/rooms/${roomId}/kick`, {
@@ -414,10 +415,9 @@ export function ModerationPanel({
const newOwner = members.find((m) => m.userId === selectedNewOwner)
if (!newOwner) return
- if (!confirm(`Transfer ownership to ${newOwner.displayName}? You will no longer be the host.`))
- return
-
+ setConfirmingTransferOwnership(false)
setActionLoading('transfer-ownership')
+
try {
const res = await fetch(`/api/arcade/rooms/${roomId}/transfer-ownership`, {
method: 'POST',
@@ -436,6 +436,7 @@ export function ModerationPanel({
showError(err instanceof Error ? err.message : 'Failed to transfer ownership')
} finally {
setActionLoading(null)
+ setSelectedNewOwner('') // Reset selection
}
}
@@ -1789,61 +1790,143 @@ export function ModerationPanel({
Transfer host privileges to another member. You will no longer be the host.
-
+ {!confirmingTransferOwnership ? (
+ <>
+
-
+
+ >
+ ) : (
+
+
+ ⚠️ Confirm Transfer to{' '}
+ {members.find((m) => m.userId === selectedNewOwner)?.displayName}?
+
+
+ You will no longer be the host and will lose moderation privileges. This
+ cannot be undone.
+
+
+
+
+
+
+ )}