1import { useState, useEffect } from 'react';
2import { useNavigate, useParams } from 'react-router-dom';
3import MainLayout from '../components/Layout/MainLayout';
4import '../styles/forms.css';
5import { apiFetch } from '../lib/api';
8 const navigate = useNavigate();
9 const { id } = useParams();
10 const [form, setForm] = useState({
15 const [loading, setLoading] = useState(false);
16 const [error, setError] = useState('');
24 async function fetchTenant() {
28 const token = localStorage.getItem('token');
29 const res = await apiFetch(`/tenants/${id}`, {
31 Authorization: `Bearer ${token}`
35 if (!res.ok) throw new Error('Failed to load tenant');
37 const data = await res.json();
39 name: data.tenant.name || '',
40 subdomain: data.tenant.subdomain || '',
41 status: data.tenant.status || 'active'
45 setError(err.message || 'Failed to load tenant');
51 async function handleSubmit(e) {
56 if (!form.name?.trim()) {
57 setError('Tenant name is required');
62 if (!id && !form.subdomain?.trim()) {
63 setError('Subdomain is required');
69 const token = localStorage.getItem('token');
75 ? { name: form.name, status: form.status }
76 : { name: form.name, subdomain: form.subdomain };
78 const res = await apiFetch(url, {
79 method: id ? 'PUT' : 'POST',
81 'Content-Type': 'application/json',
82 Authorization: `Bearer ${token}`
84 body: JSON.stringify(body)
87 const data = await res.json();
90 throw new Error(data.error || 'Failed to save tenant');
93 // After creation or update, go back to tenants list
97 setError(err.message || 'Failed to save tenant');
105 <div className="page-content">
106 <div className="page-header">
107 <h2>{id ? 'Edit Tenant' : 'Create Tenant'}</h2>
110 {error && <div className="error-message">{error}</div>}
112 <form onSubmit={handleSubmit} className="form-container">
113 <div className="form-grid">
114 <div className="form-section">
115 <h3>Tenant Information</h3>
117 <div className="form-row">
118 <div className="form-field">
119 <label>Tenant Name<span className="required">*</span></label>
125 onChange={(e) => setForm({ ...form, name: e.target.value })}
127 placeholder="e.g., Acme Corporation"
129 <span className="field-hint">The name of the organization/company</span>
133 <div className="form-row">
134 <div className="form-field">
136 Tenant Code<span className="required">*</span>
137 {id && <span style={{ color: 'var(--text-muted)', fontWeight: 'normal' }}> (Cannot be changed)</span>}
143 value={form.subdomain}
144 onChange={(e) => setForm({ ...form, subdomain: e.target.value.toLowerCase() })}
147 placeholder="e.g., acme"
148 pattern="[a-z0-9][a-z0-9-]*[a-z0-9]"
150 <span className="field-hint">
151 Lowercase letters, numbers, and hyphens only.
152 {!id && ' This will be used as a unique tenant code.'}
157 <div className="form-row">
158 <div className="form-field">
159 <label>Status</label>
162 onChange={(e) => setForm({ ...form, status: e.target.value })}
164 <option value="active">Active</option>
165 <option value="suspended">Suspended</option>
166 <option value="inactive">Inactive</option>
168 <span className="field-hint">
169 Active: Tenant can access the system.
170 Suspended: Temporary block.
171 Inactive: Permanently disabled.
177 <div className="info-box" style={{
180 backgroundColor: 'var(--bg-secondary)',
181 border: '1px solid var(--border)',
184 <h4 style={{ marginTop: 0, display: 'flex', alignItems: 'center', gap: '8px' }}>
185 <span className="material-symbols-outlined">info</span>
186 Next Steps After Creation
188 <ul style={{ marginBottom: 0, paddingLeft: '20px' }}>
189 <li>Create an owner user for this tenant</li>
190 <li>Configure tenant-specific settings</li>
191 <li>Send invitation to the tenant admin</li>
198 <div className="form-actions">
199 <button type="submit" disabled={loading} className="btn">
200 {loading ? 'Saving...' : (
202 <span className="material-symbols-outlined">save</span>
203 {id ? 'Update' : 'Create'} Tenant
207 <button type="button" className="btn" onClick={() => navigate('/tenants')}>
208 <span className="material-symbols-outlined">close</span> Cancel
217export default TenantForm;