// frontend/src/modals/bellcloud/devices/AddDeviceUserModal.jsx
// Search and add an app user to a device's user_list.
import { useState, useEffect, useRef, useCallback } from 'react'
import api from '@/lib/api'
import Modal from '@/components/ui/Modal'
import Button from '@/components/ui/Button'
import Spinner from '@/components/ui/Spinner'
// ─── User row ─────────────────────────────────────────────────────────────────
function UserRow({ user, onAdd, adding }) {
const initials = (user.display_name || user.email || '?').charAt(0).toUpperCase()
return (
{/* Avatar */}
{user.photo_url ? (
) : initials}
{/* Info */}
{user.display_name || '—'}
{user.email && (
{user.email}
)}
{/* Add button */}
onAdd(user)}
loading={adding === user.id}
style={{ flexShrink: 0 }}
>
Add
)
}
// ─── AddDeviceUserModal ───────────────────────────────────────────────────────
export default function AddDeviceUserModal({ open, deviceId, existingUserIds = [], onClose, onAdded }) {
const [query, setQuery] = useState('')
const [results, setResults] = useState([])
const [loading, setLoading] = useState(false)
const [adding, setAdding] = useState(null)
const [error, setError] = useState('')
const debounceRef = useRef(null)
const inputRef = useRef(null)
// Reset on open
useEffect(() => {
if (!open) return
setQuery('')
setResults([])
setError('')
setAdding(null)
setTimeout(() => inputRef.current?.focus(), 80)
}, [open])
const search = useCallback(async (q) => {
setLoading(true)
setError('')
try {
const data = await api.get(`/devices/${deviceId}/user-search?q=${encodeURIComponent(q)}`)
// Filter out users already on the device
const existing = new Set(existingUserIds)
setResults((data.results || []).filter(u => !existing.has(u.id)))
} catch (err) {
setError(err.message || 'Search failed.')
setResults([])
} finally {
setLoading(false)
}
}, [deviceId, existingUserIds])
const handleInput = (e) => {
const val = e.target.value
setQuery(val)
clearTimeout(debounceRef.current)
debounceRef.current = setTimeout(() => search(val), 280)
}
// Load all users on open (empty query)
useEffect(() => {
if (open && deviceId) search('')
}, [open, deviceId, search])
const handleAdd = async (user) => {
setAdding(user.id)
setError('')
try {
await api.post(`/devices/${deviceId}/user-list`, { user_id: user.id })
onAdded(user)
// Remove from results so it disappears immediately
setResults(prev => prev.filter(u => u.id !== user.id))
} catch (err) {
setError(err.message || 'Failed to add user.')
} finally {
setAdding(null)
}
}
return (
{/* Search */}
{error && (
{error}
)}
{/* Results */}
{loading ? (
) : results.length === 0 ? (
{query ? 'No users found matching your search.' : 'All users already added or no users exist.'}
) : (
results.map(user => (
))
)}
{/* Footer */}
Close
)
}