const { useState, useEffect, useCallback, useRef } = React;
const api = (path, opts = {}) =>
fetch(`/admin${path}`, {
headers: { 'Content-Type': 'application/json' },
...opts,
body: opts.body ? JSON.stringify(opts.body) : undefined,
}).then(r => r.json());
// ─── Login Screen ─────────────────────────────────────
function LoginPage({ onLogin }) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [mfaCode, setMfaCode] = useState('');
const [mfaToken, setMfaToken] = useState(null);
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const handleLogin = async (e) => {
e.preventDefault();
setError(''); setLoading(true);
const data = await api('/api/login', { method: 'POST', body: { email, password } });
setLoading(false);
if (data.error) return setError(data.error);
if (data.mfaRequired) return setMfaToken(data.mfaToken);
if (data.success) onLogin(data.user);
};
const handleMfa = async (e) => {
e.preventDefault();
setError(''); setLoading(true);
const data = await api('/api/mfa/verify', { method: 'POST', body: { mfaToken, code: mfaCode } });
setLoading(false);
if (data.error) return setError(data.error);
if (data.success) onLogin(data.user);
};
return (
<div className="login-container">
<div className="login-box">
<div style={{ textAlign: 'center', marginBottom: '1.5rem' }}>
<img src="https://cdn.buckheadenergy.com/images/buckhead_energy.webp" alt="Buckhead Energy" style={{ height: 40 }} />
</div>
<h1>{mfaToken ? 'Two-Factor Authentication' : 'Admin Login'}</h1>
<p>{mfaToken ? 'Enter the code from your authenticator app' : 'Sign in to your admin dashboard'}</p>
{error && <div className="error-msg"><i className="fas fa-exclamation-circle" style={{ marginRight: 6 }}></i>{error}</div>}
{!mfaToken ? (
<form onSubmit={handleLogin}>
<div className="form-group"><label>Email</label><input type="email" value={email} onChange={e => setEmail(e.target.value)} autoFocus required /></div>
<div className="form-group"><label>Password</label><input type="password" value={password} onChange={e => setPassword(e.target.value)} required /></div>
<button type="submit" className="btn btn-primary" disabled={loading}>{loading ? 'Signing in...' : 'Sign In'}</button>
</form>
) : (
<form onSubmit={handleMfa}>
<div className="form-group"><label>Authentication Code</label><input type="text" value={mfaCode} onChange={e => setMfaCode(e.target.value)} autoFocus maxLength={8} placeholder="000000" autoComplete="one-time-code" /></div>
<button type="submit" className="btn btn-primary" disabled={loading}>{loading ? 'Verifying...' : 'Verify'}</button>
</form>
)}
</div>
</div>
);
}
// ─── Intelligence Dashboard ──────────────────────────
function IntelligencePage() {
const [data, setData] = useState(null);
const [days, setDays] = useState(7);
const [active, setActive] = useState(0);
const chartRef = useRef(null);
const chartInstance = useRef(null);
const load = useCallback(async () => {
const d = await api(`/api/analytics?days=${days}`);
if (!d.error) { setData(d); setActive(d.activeVisitors || 0); }
}, [days]);
useEffect(() => { load(); }, [load]);
// Poll active visitors
useEffect(() => {
const interval = setInterval(async () => {
const d = await api('/api/analytics/active');
if (d.activeVisitors !== undefined) setActive(d.activeVisitors);
}, 30000);
return () => clearInterval(interval);
}, []);
// Chart
useEffect(() => {
if (!data?.dailyStats?.length || !chartRef.current) return;
if (chartInstance.current) chartInstance.current.destroy();
const ctx = chartRef.current.getContext('2d');
chartInstance.current = new Chart(ctx, {
type: 'line',
data: {
labels: data.dailyStats.map(d => new Date(d.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })),
datasets: [
{ label: 'Pageviews', data: data.dailyStats.map(d => d.pageviews), borderColor: '#30cb32', backgroundColor: 'rgba(48,203,50,0.1)', fill: true, tension: 0.3 },
{ label: 'Visitors', data: data.dailyStats.map(d => d.visitors), borderColor: '#60a5fa', backgroundColor: 'rgba(96,165,250,0.1)', fill: true, tension: 0.3 },
],
},
options: {
responsive: true, maintainAspectRatio: false,
plugins: { legend: { labels: { color: '#9ca0b4', font: { size: 12 } } } },
scales: {
x: { grid: { color: '#2e3348' }, ticks: { color: '#9ca0b4', font: { size: 11 } } },
y: { grid: { color: '#2e3348' }, ticks: { color: '#9ca0b4', font: { size: 11 } } },
},
},
});
}, [data]);
if (!data) return <div className="loading"><div className="spinner"></div>Loading analytics...</div>;
const timeAgo = (ts) => {
const diff = Date.now() - new Date(ts).getTime();
if (diff < 60000) return 'just now';
if (diff < 3600000) return `${Math.floor(diff/60000)}m ago`;
if (diff < 86400000) return `${Math.floor(diff/3600000)}h ago`;
return `${Math.floor(diff/86400000)}d ago`;
};
return (
<>
<div className="page-header">
<h1><i className="fas fa-chart-line" style={{ marginRight: 10, color: 'var(--green2)' }}></i>Intelligence</h1>
<select value={days} onChange={e => setDays(Number(e.target.value))}>
<option value={1}>Today</option>
<option value={7}>Last 7 days</option>
<option value={30}>Last 30 days</option>
<option value={90}>Last 90 days</option>
</select>
</div>
<div className="stats-grid">
<div className="stat-card">
<div className="stat-label"><span className="active-pulse"></span>Active Now</div>
<div className="stat-value green">{active}</div>
</div>
<div className="stat-card">
<div className="stat-label">Unique Visitors</div>
<div className="stat-value blue">{data.uniqueVisitors?.toLocaleString()}</div>
</div>
<div className="stat-card">
<div className="stat-label">Total Pageviews</div>
<div className="stat-value">{data.totalPageviews?.toLocaleString()}</div>
</div>
<div className="stat-card">
<div className="stat-label">Pages / Visitor</div>
<div className="stat-value">{data.uniqueVisitors ? (data.totalPageviews / data.uniqueVisitors).toFixed(1) : '0'}</div>
</div>
</div>
<div className="card">
<div className="card-header"><h3>Traffic Overview</h3></div>
<div className="chart-container"><canvas ref={chartRef}></canvas></div>
</div>
<div className="grid-2">
<div className="card">
<div className="card-header"><h3>Top Pages</h3><span className="badge">{data.topPages?.length} pages</span></div>
<table className="data-table">
<thead><tr><th>Page</th><th className="num">Views</th><th className="num">Unique</th></tr></thead>
<tbody>
{data.topPages?.map((p, i) => (
<tr key={i}>
<td><span className="path-link">{p.path}</span></td>
<td className="num">{Number(p.views).toLocaleString()}</td>
<td className="num">{Number(p.unique_visitors).toLocaleString()}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="card">
<div className="card-header"><h3>Top Referrers</h3><span className="badge">{data.topReferrers?.length} sources</span></div>
<table className="data-table">
<thead><tr><th>Source</th><th className="num">Visits</th><th className="num">Unique</th></tr></thead>
<tbody>
{data.topReferrers?.map((r, i) => (
<tr key={i}>
<td>{r.referrer}</td>
<td className="num">{Number(r.visits).toLocaleString()}</td>
<td className="num">{Number(r.unique_visitors).toLocaleString()}</td>
</tr>
))}
{!data.topReferrers?.length && <tr><td colSpan={3} style={{ color: 'var(--text2)' }}>No referrer data yet</td></tr>}
</tbody>
</table>
</div>
</div>
<div className="card">
<div className="card-header"><h3>Recent Visitors</h3><span className="badge">Last 50</span></div>
{data.recentVisitors?.map((v, i) => (
<div className="visitor-row" key={i}>
<div className="visitor-dot"></div>
<div className="visitor-ip">{v.ip}</div>
<div className="visitor-country">{v.country || '—'}</div>
<div className="visitor-page">{v.path}</div>
<div className="visitor-time">{timeAgo(v.created_at)}</div>
</div>
))}
</div>
</>
);
}
// ─── Settings Page ───────────────────────────────────
function SettingsPage({ user }) {
const [mfaSetup, setMfaSetup] = useState(null);
const [code, setCode] = useState('');
const [backupCodes, setBackupCodes] = useState(null);
const [error, setError] = useState('');
const [msg, setMsg] = useState('');
const setupMfa = async () => {
setError('');
const data = await api('/api/mfa/setup', { method: 'POST' });
if (data.error) return setError(data.error);
setMfaSetup(data);
};
const confirmMfa = async (e) => {
e.preventDefault();
setError('');
const data = await api('/api/mfa/confirm', { method: 'POST', body: { code } });
if (data.error) return setError(data.error);
setBackupCodes(data.backupCodes);
setMfaSetup(null);
setMsg('MFA enabled successfully');
};
return (
<>
<div className="page-header"><h1><i className="fas fa-cog" style={{ marginRight: 10 }}></i>Settings</h1></div>
<div className="card">
<div className="card-header"><h3>Two-Factor Authentication</h3></div>
{error && <div className="error-msg" style={{ marginBottom: '1rem' }}>{error}</div>}
{msg && <div style={{ background: 'rgba(48,203,50,0.1)', border: '1px solid rgba(48,203,50,0.3)', color: 'var(--green2)', padding: '8px 12px', borderRadius: 8, fontSize: 13, marginBottom: '1rem' }}><i className="fas fa-check" style={{ marginRight: 6 }}></i>{msg}</div>}
{user?.mfaEnabled && !mfaSetup && !backupCodes && (
<p style={{ color: 'var(--green2)' }}><i className="fas fa-shield-alt" style={{ marginRight: 6 }}></i>MFA is enabled</p>
)}
{!user?.mfaEnabled && !mfaSetup && (
<div>
<p style={{ color: 'var(--text2)', marginBottom: '1rem', fontSize: 14 }}>Protect your account with an authenticator app</p>
<button className="btn btn-primary" style={{ width: 'auto' }} onClick={setupMfa}><i className="fas fa-shield-alt" style={{ marginRight: 6 }}></i>Enable MFA</button>
</div>
)}
{mfaSetup && (
<div className="mfa-qr">
<p style={{ fontSize: 14 }}>Scan this QR code with your authenticator app:</p>
<img src={mfaSetup.qrCode} alt="MFA QR Code" width={200} height={200} />
<p style={{ fontSize: 12, color: 'var(--text2)', marginBottom: '1rem' }}>Secret: <code>{mfaSetup.secret}</code></p>
<form onSubmit={confirmMfa} style={{ maxWidth: 300, margin: '0 auto' }}>
<div className="form-group"><label>Enter code to confirm</label><input type="text" value={code} onChange={e => setCode(e.target.value)} maxLength={6} placeholder="000000" autoFocus /></div>
<button type="submit" className="btn btn-primary">Confirm</button>
</form>
</div>
)}
{backupCodes && (
<div>
<p style={{ fontSize: 14, marginBottom: '0.5rem', fontWeight: 600 }}>Save your backup codes:</p>
<p style={{ fontSize: 12, color: 'var(--text2)', marginBottom: '1rem' }}>Each code can only be used once. Store them securely.</p>
<div className="backup-codes">
{backupCodes.map((c, i) => <code key={i}>{c}</code>)}
</div>
</div>
)}
</div>
</>
);
}
// ─── Tax Rolls Search ────────────────────────────────
function TaxRollsPage() {
const [form, setForm] = useState({ ownerName: '', ownerAddress: '', ownerCity: '', county: '', appraisalYear: '2024' });
const [results, setResults] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [sortCol, setSortCol] = useState(null);
const [sortDir, setSortDir] = useState('asc');
const set = (k, v) => setForm(prev => ({ ...prev, [k]: v }));
const handleSearch = async (e) => {
e.preventDefault();
if (!form.ownerName && !form.ownerAddress && !form.ownerCity && !form.county) {
return setError('Enter at least one search field');
}
setError(''); setLoading(true); setResults(null);
const data = await api('/api/tax-rolls/search', { method: 'POST', body: form });
setLoading(false);
if (data.error) return setError(data.error);
setResults(data.results || []);
setSortCol(null);
};
const sorted = results ? [...results].sort((a, b) => {
if (sortCol === null) return 0;
const cols = ['OWNER_NAME', 'ADDRESS', 'CITY', 'STATE', 'ZIP', 'AVG_INTEREST', 'INTEREST_COUNT', 'TOTAL_VALUE', 'TOTAL_ESTIMATED_REVENUE', 'COUNTIES'];
const key = cols[sortCol];
const numCols = [5, 6, 7, 8, 9];
let av = a[key], bv = b[key];
if (numCols.includes(sortCol)) { av = Number(av) || 0; bv = Number(bv) || 0; }
else { av = String(av || '').toLowerCase(); bv = String(bv || '').toLowerCase(); }
if (av < bv) return sortDir === 'asc' ? -1 : 1;
if (av > bv) return sortDir === 'asc' ? 1 : -1;
return 0;
}) : null;
const toggleSort = (col) => {
if (sortCol === col) setSortDir(d => d === 'asc' ? 'desc' : 'asc');
else { setSortCol(col); setSortDir('asc'); }
};
const sortIcon = (col) => sortCol === col ? (sortDir === 'asc' ? ' ↑' : ' ↓') : ' ⇅';
const fieldStyle = { width: '100%', padding: '8px 12px', background: 'var(--surface2)', border: '1px solid var(--border)', borderRadius: 6, color: 'var(--text)', fontSize: 13 };
const labelStyle = { display: 'block', fontSize: 12, color: 'var(--text2)', marginBottom: 4 };
return (
<>
<div className="page-header">
<h1><i className="fas fa-database" style={{ marginRight: 10, color: 'var(--green2)' }}></i>Texas Tax Rolls</h1>
</div>
<div className="card" style={{ marginBottom: '1.5rem' }}>
<div className="card-header"><h3>Search</h3></div>
<form onSubmit={handleSearch}>
<div className="grid-2" style={{ gap: '1rem', marginBottom: '1rem' }}>
<div><label style={labelStyle}>Owner Name</label><input style={fieldStyle} value={form.ownerName} onChange={e => set('ownerName', e.target.value)} placeholder='e.g. "BUCKHEAD ENERGY LLC"' /></div>
<div><label style={labelStyle}>Address</label><input style={fieldStyle} value={form.ownerAddress} onChange={e => set('ownerAddress', e.target.value)} /></div>
<div><label style={labelStyle}>City</label><input style={fieldStyle} value={form.ownerCity} onChange={e => set('ownerCity', e.target.value)} /></div>
<div><label style={labelStyle}>County</label><input style={fieldStyle} value={form.county} onChange={e => set('county', e.target.value)} placeholder="e.g. Harris" /></div>
<div><label style={labelStyle}>Appraisal Year</label>
<select style={fieldStyle} value={form.appraisalYear} onChange={e => set('appraisalYear', e.target.value)}>
<option value="">All Years</option>
<option value="2024">2024</option>
<option value="2023">2023</option>
<option value="2022">2022</option>
<option value="2021">2021</option>
<option value="2020">2020</option>
</select>
</div>
<div style={{ display: 'flex', alignItems: 'flex-end' }}>
<button type="submit" className="btn btn-primary" style={{ width: 'auto', padding: '8px 20px' }} disabled={loading}>
{loading ? <><i className="fas fa-spinner fa-spin" style={{ marginRight: 6 }}></i>Searching...</> : <><i className="fas fa-search" style={{ marginRight: 6 }}></i>Search</>}
</button>
</div>
</div>
<p style={{ fontSize: 12, color: 'var(--text2)', margin: 0 }}>Tip: Use quotes for exact match, e.g. "SMITH JOHN"</p>
</form>
</div>
{error && <div className="error-msg" style={{ marginBottom: '1rem' }}>{error}</div>}
{sorted && (
<div className="card">
<div className="card-header">
<h3>Results</h3>
<span className="badge">{sorted.length}{sorted.length >= 1000 ? '+ (limit reached)' : ''} records</span>
</div>
<div style={{ overflowX: 'auto' }}>
<table className="data-table">
<thead>
<tr>
{['Owner Name', 'Address', 'City', 'State', 'ZIP', 'Avg Interest', 'Interests', 'Total Value', 'Est. Revenue', 'Counties'].map((h, i) => (
<th key={i} onClick={() => toggleSort(i)} style={{ cursor: 'pointer', whiteSpace: 'nowrap', userSelect: 'none' }} className={i >= 5 ? 'num' : ''}>
{h}{sortIcon(i)}
</th>
))}
</tr>
</thead>
<tbody>
{sorted.map((r, i) => (
<tr key={i}>
<td style={{ fontWeight: 500 }}>{r.OWNER_NAME}</td>
<td>{r.ADDRESS}</td>
<td>{r.CITY}</td>
<td>{r.STATE}</td>
<td>{r.ZIP}</td>
<td className="num">{r.AVG_INTEREST ? Number(r.AVG_INTEREST).toFixed(6) : '0'}</td>
<td className="num">{(r.INTEREST_COUNT || 0).toLocaleString()}</td>
<td className="num">{Number(r.TOTAL_VALUE || 0).toLocaleString()}</td>
<td className="num">{Math.round(Number(r.TOTAL_ESTIMATED_REVENUE || 0)).toLocaleString()}</td>
<td className="num">{r.COUNTIES}</td>
</tr>
))}
{sorted.length === 0 && <tr><td colSpan={10} style={{ color: 'var(--text2)', textAlign: 'center', padding: '2rem' }}>No results found</td></tr>}
</tbody>
</table>
</div>
</div>
)}
</>
);
}
// ─── Offers Page ─────────────────────────────────────
function OffersPage() {
const [offers, setOffers] = useState([]);
const [loading, setLoading] = useState(true);
const [editing, setEditing] = useState(null);
const [creating, setCreating] = useState(false);
const [error, setError] = useState('');
const [success, setSuccess] = useState('');
const load = useCallback(async () => {
const data = await api('/api/offers');
if (data.offers) setOffers(data.offers);
setLoading(false);
}, []);
useEffect(() => { load(); }, [load]);
const emptyOffer = { name: '', county: '', offerAmount: '', email: '', cellPhone: '', address1: '', address2: '', mineralHolders: '', notes: '', accessCode: '' };
const handleSave = async (offer, isNew) => {
setError(''); setSuccess('');
const method = isNew ? 'POST' : 'PUT';
const path = isNew ? '/api/offers' : `/api/offers/${offer.id}`;
const data = await api(path, { method, body: offer });
if (data.error) return setError(data.error);
setSuccess(isNew ? 'Offer created' : 'Offer updated');
setEditing(null); setCreating(false);
load();
};
const handleDelete = async (id) => {
if (!confirm('Delete this offer?')) return;
const data = await api(`/api/offers/${id}`, { method: 'DELETE' });
if (data.error) return setError(data.error);
setSuccess('Offer deleted');
load();
};
const formatMoney = (v) => {
const num = parseFloat(v);
return isNaN(num) ? '$0.00' : '$' + num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
};
if (loading) return <div className="loading"><div className="spinner"></div>Loading offers...</div>;
const editingOffer = creating ? emptyOffer : editing;
return (
<>
<div className="page-header">
<h1><i className="fas fa-file-invoice-dollar" style={{ marginRight: 10, color: 'var(--green2)' }}></i>Offers</h1>
{!editingOffer && <button className="btn btn-primary" style={{ width: 'auto', padding: '8px 16px' }} onClick={() => setCreating(true)}><i className="fas fa-plus" style={{ marginRight: 6 }}></i>New Offer</button>}
</div>
{error && <div className="error-msg" style={{ marginBottom: '1rem' }}>{error}</div>}
{success && <div style={{ background: 'rgba(48,203,50,0.1)', border: '1px solid rgba(48,203,50,0.3)', color: 'var(--green2)', padding: '8px 12px', borderRadius: 8, fontSize: 13, marginBottom: '1rem' }}><i className="fas fa-check" style={{ marginRight: 6 }}></i>{success}</div>}
{editingOffer ? (
<OfferForm offer={editingOffer} isNew={creating} onSave={handleSave} onCancel={() => { setEditing(null); setCreating(false); }} />
) : (
<div className="card">
<table className="data-table">
<thead>
<tr>
<th>Code</th>
<th>Name</th>
<th>County</th>
<th className="num">Amount</th>
<th>Email</th>
<th>Phone</th>
<th>Accessed</th>
<th>Accepted</th>
<th></th>
</tr>
</thead>
<tbody>
{offers.map(o => (
<tr key={o.id}>
<td><code style={{ color: 'var(--green2)', fontSize: 12 }}>{o.access_code}</code></td>
<td>{o.name}</td>
<td>{o.county}</td>
<td className="num">{formatMoney(o.offer_amount)}</td>
<td style={{ fontSize: 12 }}>{o.email || '—'}</td>
<td style={{ fontSize: 12 }}>{o.cell_phone || '—'}</td>
<td>{o.offer_accessed ? <span style={{ color: 'var(--green2)' }}>Yes</span> : <span style={{ color: 'var(--text2)' }}>No</span>}</td>
<td>{o.accepted ? <span style={{ color: 'var(--green2)', fontWeight: 600 }}>Accepted</span> : <span style={{ color: 'var(--text2)' }}>Pending</span>}</td>
<td style={{ whiteSpace: 'nowrap' }}>
<button className="btn" style={{ padding: '4px 8px', fontSize: 11, background: 'var(--surface2)', color: 'var(--text2)', marginRight: 4 }} onClick={() => { setEditing(o); setCreating(false); }}><i className="fas fa-edit"></i></button>
<button className="btn" style={{ padding: '4px 8px', fontSize: 11, background: 'rgba(239,68,68,0.15)', color: 'var(--red)' }} onClick={() => handleDelete(o.id)}><i className="fas fa-trash"></i></button>
</td>
</tr>
))}
{offers.length === 0 && <tr><td colSpan={9} style={{ color: 'var(--text2)', textAlign: 'center', padding: '2rem' }}>No offers yet</td></tr>}
</tbody>
</table>
</div>
)}
</>
);
}
function OfferForm({ offer, isNew, onSave, onCancel }) {
const [form, setForm] = useState({
name: offer.name || '',
county: offer.county || '',
offerAmount: offer.offer_amount || offer.offerAmount || '',
email: offer.email || '',
cellPhone: offer.cell_phone || offer.cellPhone || '',
address1: offer.address1 || offer.address_1 || '',
address2: offer.address2 || offer.address_2 || '',
mineralHolders: offer.mineral_holders || offer.mineralHolders || '',
notes: offer.notes || '',
accessCode: offer.access_code || offer.accessCode || '',
});
const set = (k, v) => setForm(prev => ({ ...prev, [k]: v }));
const handleSubmit = (e) => {
e.preventDefault();
onSave({ ...form, id: offer.id }, isNew);
};
const fieldStyle = { width: '100%', padding: '8px 12px', background: 'var(--surface2)', border: '1px solid var(--border)', borderRadius: 6, color: 'var(--text)', fontSize: 13 };
const labelStyle = { display: 'block', fontSize: 12, color: 'var(--text2)', marginBottom: 4 };
return (
<div className="card">
<div className="card-header"><h3>{isNew ? 'Create Offer' : 'Edit Offer'}</h3></div>
<form onSubmit={handleSubmit}>
<div className="grid-2" style={{ gap: '1rem', marginBottom: '1rem' }}>
<div><label style={labelStyle}>Name *</label><input style={fieldStyle} value={form.name} onChange={e => set('name', e.target.value)} required /></div>
<div><label style={labelStyle}>County *</label><input style={fieldStyle} value={form.county} onChange={e => set('county', e.target.value)} required /></div>
<div><label style={labelStyle}>Offer Amount *</label><input style={fieldStyle} value={form.offerAmount} onChange={e => set('offerAmount', e.target.value)} required placeholder="$0.00" /></div>
<div><label style={labelStyle}>Access Code</label><input style={fieldStyle} value={form.accessCode} onChange={e => set('accessCode', e.target.value)} placeholder="Auto-generated if blank" /></div>
<div><label style={labelStyle}>Email</label><input style={fieldStyle} type="email" value={form.email} onChange={e => set('email', e.target.value)} /></div>
<div><label style={labelStyle}>Phone</label><input style={fieldStyle} type="tel" value={form.cellPhone} onChange={e => set('cellPhone', e.target.value)} /></div>
<div><label style={labelStyle}>Address 1</label><input style={fieldStyle} value={form.address1} onChange={e => set('address1', e.target.value)} /></div>
<div><label style={labelStyle}>Address 2</label><input style={fieldStyle} value={form.address2} onChange={e => set('address2', e.target.value)} /></div>
</div>
<div style={{ marginBottom: '1rem' }}><label style={labelStyle}>Mineral Holders</label><textarea style={{ ...fieldStyle, minHeight: 60 }} value={form.mineralHolders} onChange={e => set('mineralHolders', e.target.value)}></textarea></div>
<div style={{ marginBottom: '1rem' }}><label style={labelStyle}>Notes</label><textarea style={{ ...fieldStyle, minHeight: 60 }} value={form.notes} onChange={e => set('notes', e.target.value)}></textarea></div>
<div style={{ display: 'flex', gap: 8 }}>
<button type="submit" className="btn btn-primary" style={{ width: 'auto', padding: '8px 20px' }}>{isNew ? 'Create Offer' : 'Save Changes'}</button>
<button type="button" className="btn" style={{ padding: '8px 20px', background: 'var(--surface2)', color: 'var(--text2)' }} onClick={onCancel}>Cancel</button>
</div>
</form>
</div>
);
}
// ─── App Shell ───────────────────────────────────────
function App() {
const [user, setUser] = useState(null);
const [page, setPage] = useState('intelligence');
const [checking, setChecking] = useState(true);
useEffect(() => {
api('/api/me').then(data => {
if (data.email) setUser(data);
setChecking(false);
}).catch(() => setChecking(false));
}, []);
const logout = async () => {
await api('/api/logout', { method: 'POST' });
setUser(null);
};
if (checking) return <div className="loading"><div className="spinner"></div>Loading...</div>;
if (!user) return <LoginPage onLogin={(u) => { setUser(u); api('/api/me').then(d => { if (d.email) setUser(d); }); }} />;
return (
<div className="admin-layout">
<nav className="sidebar">
<div className="sidebar-logo">
<img src="https://cdn.buckheadenergy.com/images/buckhead_energy_circle.webp" alt="" />
<span>Buckhead Admin</span>
</div>
<ul className="sidebar-nav">
<li><a href="#" className={page === 'intelligence' ? 'active' : ''} onClick={e => { e.preventDefault(); setPage('intelligence'); }}><i className="fas fa-chart-line"></i>Intelligence</a></li>
<li><a href="#" className={page === 'tax-rolls' ? 'active' : ''} onClick={e => { e.preventDefault(); setPage('tax-rolls'); }}><i className="fas fa-database"></i>Tax Rolls</a></li>
<li><a href="#" className={page === 'offers' ? 'active' : ''} onClick={e => { e.preventDefault(); setPage('offers'); }}><i className="fas fa-file-invoice-dollar"></i>Offers</a></li>
<li><a href="#" className={page === 'settings' ? 'active' : ''} onClick={e => { e.preventDefault(); setPage('settings'); }}><i className="fas fa-cog"></i>Settings</a></li>
</ul>
</nav>
<main className="main-content">
<div className="user-bar">
<span><i className="fas fa-user" style={{ marginRight: 6 }}></i>{user.email}</span>
{user.mfaEnabled && <span style={{ color: 'var(--green2)' }}><i className="fas fa-shield-alt" style={{ marginRight: 4 }}></i>MFA</span>}
<button onClick={logout}><i className="fas fa-sign-out-alt" style={{ marginRight: 4 }}></i>Logout</button>
</div>
{page === 'intelligence' && <IntelligencePage />}
{page === 'tax-rolls' && <TaxRollsPage />}
{page === 'offers' && <OffersPage />}
{page === 'settings' && <SettingsPage user={user} />}
</main>
</div>
);
}
ReactDOM.createRoot(document.getElementById('admin-root')).render(<App />);