1import { useState, useEffect } from 'react';
2import { apiFetch } from '../lib/api';
5export default function AssignDeviceModal({ isOpen, onClose, device, onAssigned }) {
6 const [tenants, setTenants] = useState([]);
7 const [customers, setCustomers] = useState([]);
8 const [selectedTenant, setSelectedTenant] = useState('');
9 const [selectedCustomer, setSelectedCustomer] = useState('');
10 const [moveMesh, setMoveMesh] = useState(true);
11 const [loading, setLoading] = useState(false);
12 const [error, setError] = useState('');
22 fetchCustomers(selectedTenant);
25 setSelectedCustomer('');
29 const fetchTenants = async () => {
31 const res = await apiFetch('/tenants');
32 if (!res.ok) throw new Error('Failed to fetch tenants');
33 const data = await res.json();
34 setTenants(data.tenants || []);
36 console.error('Error fetching tenants:', err);
37 setError('Failed to load tenants');
41 const fetchCustomers = async (tenantId) => {
43 const res = await apiFetch(`/customers?tenant=${tenantId}`);
44 if (!res.ok) throw new Error('Failed to fetch customers');
45 const data = await res.json();
46 setCustomers(data.customers || []);
48 console.error('Error fetching customers:', err);
53 const handleAssign = async () => {
54 if (!selectedTenant) {
55 setError('Please select a tenant');
63 const res = await apiFetch('/monitoring/assign-device', {
65 headers: { 'Content-Type': 'application/json' },
66 body: JSON.stringify({
67 nodeId: device.meshcentral_nodeid,
68 tenantId: selectedTenant,
69 customerId: selectedCustomer || null,
75 const errData = await res.json();
76 throw new Error(errData.error || 'Failed to assign device');
79 const result = await res.json();
80 console.log('Device assigned:', result);
88 console.error('Error assigning device:', err);
89 setError(err.message);
95 if (!isOpen) return null;
98 <div className="modal-overlay" onClick={onClose}>
99 <div className="modal-content" onClick={(e) => e.stopPropagation()}>
100 <div className="modal-header">
101 <h2>Assign Device to Tenant</h2>
102 <button className="modal-close" onClick={onClose}>
103 <span className="material-symbols-outlined">close</span>
107 <div className="modal-body">
109 <div className="error-message" style={{ marginBottom: '1rem' }}>
114 <div style={{ marginBottom: '1rem', padding: '1rem', background: 'var(--bg-secondary)', borderRadius: '8px' }}>
115 <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '0.5rem' }}>
116 <span className="material-symbols-outlined" style={{ color: 'var(--primary)' }}>
119 <strong>{device.hostname || 'Unknown Device'}</strong>
121 <div style={{ fontSize: '0.875rem', color: 'var(--text-secondary)' }}>
122 <div>Platform: {device.platform || device.os || 'Unknown'}</div>
123 <div style={{ fontSize: '0.75rem', fontFamily: 'monospace', marginTop: '0.25rem' }}>
124 Node ID: {device.meshcentral_nodeid?.substring(0, 40)}...
129 <div className="form-group">
130 <label htmlFor="tenant">
131 Tenant <span style={{ color: 'red' }}>*</span>
135 value={selectedTenant}
136 onChange={(e) => setSelectedTenant(e.target.value)}
139 <option value="">-- Select Tenant --</option>
140 {tenants.map((tenant) => (
141 <option key={tenant.tenant_id} value={tenant.tenant_id}>
148 <div className="form-group">
149 <label htmlFor="customer">Customer (Optional)</label>
152 value={selectedCustomer}
153 onChange={(e) => setSelectedCustomer(e.target.value)}
154 disabled={loading || !selectedTenant}
156 <option value="">-- No Customer --</option>
157 {customers.map((customer) => (
158 <option key={customer.customer_id} value={customer.customer_id}>
163 {!selectedTenant && (
164 <small style={{ color: 'var(--text-secondary)', fontSize: '0.75rem' }}>
165 Select a tenant first to load customers
170 <div className="form-group">
171 <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer' }}>
175 onChange={(e) => setMoveMesh(e.target.checked)}
178 <span>Move device to tenant's mesh group</span>
180 <small style={{ color: 'var(--text-secondary)', fontSize: '0.75rem', marginLeft: '1.5rem' }}>
181 Recommended: Organizes devices by tenant in MeshCentral
186 <div className="modal-footer">
187 <button className="btn" onClick={onClose} disabled={loading}>
191 className="btn primary"
192 onClick={handleAssign}
193 disabled={loading || !selectedTenant}
195 {loading ? 'Assigning...' : 'Assign Device'}