Surface actual error message when user creation fails
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,10 @@ export default function Companies() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [showNew, setShowNew] = useState(false);
|
||||
const [newForm, setNewForm] = useState({ name: '', phone: '', address: '' });
|
||||
const [showNewUser, setShowNewUser] = useState(false);
|
||||
const [userForm, setUserForm] = useState({ name: '', email: '', password: '', company_id: '' });
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [userError, setUserError] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
load();
|
||||
@@ -49,6 +52,29 @@ export default function Companies() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreateUser = async (e) => {
|
||||
e.preventDefault();
|
||||
setUserError('');
|
||||
setSaving(true);
|
||||
const { data, error } = await supabase.functions.invoke('create-user', {
|
||||
body: {
|
||||
name: userForm.name.trim(),
|
||||
email: userForm.email.trim(),
|
||||
password: userForm.password,
|
||||
company_id: userForm.company_id || null,
|
||||
},
|
||||
});
|
||||
setSaving(false);
|
||||
const errMsg = data?.error || error?.message || (error ? JSON.stringify(error) : null);
|
||||
if (errMsg) {
|
||||
setUserError(errMsg);
|
||||
return;
|
||||
}
|
||||
setShowNewUser(false);
|
||||
setUserForm({ name: '', email: '', password: '', company_id: '' });
|
||||
load();
|
||||
};
|
||||
|
||||
if (loading) return <Layout><p style={{ padding: 24, color: 'var(--text-muted)' }}>Loading...</p></Layout>;
|
||||
|
||||
const unassigned = profiles.filter(p => !p.company_id);
|
||||
@@ -59,7 +85,7 @@ export default function Companies() {
|
||||
<div>
|
||||
<div className="page-title">Companies</div>
|
||||
<div className="page-subtitle">
|
||||
{companies.length} company{companies.length !== 1 ? 'ies' : ''}
|
||||
{companies.length} {companies.length !== 1 ? 'companies' : 'company'}
|
||||
{unassigned.length > 0 && (
|
||||
<span style={{ marginLeft: 10, color: 'var(--danger)', fontWeight: 600 }}>
|
||||
· {unassigned.length} unassigned user{unassigned.length !== 1 ? 's' : ''}
|
||||
@@ -67,10 +93,56 @@ export default function Companies() {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<button className="btn btn-primary" onClick={() => setShowNew(s => !s)}>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<button className="btn btn-outline" onClick={() => { setShowNewUser(s => !s); setShowNew(false); setUserError(''); }}>
|
||||
{showNewUser ? 'Cancel' : '+ New User'}
|
||||
</button>
|
||||
<button className="btn btn-primary" onClick={() => { setShowNew(s => !s); setShowNewUser(false); }}>
|
||||
{showNew ? 'Cancel' : '+ New Company'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showNewUser && (
|
||||
<div className="card" style={{ marginBottom: 24, maxWidth: 480 }}>
|
||||
<div className="card-title">New User</div>
|
||||
<form onSubmit={handleCreateUser}>
|
||||
<div className="grid-2">
|
||||
<div className="form-group">
|
||||
<label>Full Name *</label>
|
||||
<input type="text" placeholder="Jane Smith" value={userForm.name}
|
||||
onChange={e => setUserForm(f => ({ ...f, name: e.target.value }))} required autoFocus />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Email *</label>
|
||||
<input type="email" placeholder="jane@acme.com" value={userForm.email}
|
||||
onChange={e => setUserForm(f => ({ ...f, email: e.target.value }))} required />
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-2">
|
||||
<div className="form-group">
|
||||
<label>Password *</label>
|
||||
<input type="password" placeholder="Temporary password" value={userForm.password}
|
||||
onChange={e => setUserForm(f => ({ ...f, password: e.target.value }))} required minLength={6} />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Assign to Company</label>
|
||||
<select value={userForm.company_id} onChange={e => setUserForm(f => ({ ...f, company_id: e.target.value }))}>
|
||||
<option value="">No company yet</option>
|
||||
{companies.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{userError && <p style={{ fontSize: 13, color: 'var(--danger)', marginBottom: 12 }}>{userError}</p>}
|
||||
<div className="action-buttons">
|
||||
<button type="submit" className="btn btn-primary" disabled={saving}>
|
||||
{saving ? 'Creating...' : 'Create User'}
|
||||
</button>
|
||||
<button type="button" className="btn btn-outline" onClick={() => setShowNewUser(false)}>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showNew && (
|
||||
<div className="card" style={{ marginBottom: 24, maxWidth: 480 }}>
|
||||
|
||||
Reference in New Issue
Block a user