fix: crash bugs in TeamInvoices + faster load

Bug fixes:
- TeamInvoices: add useAuth import/currentUser (invoice-create crash)
- TeamInvoices: setChartYear -> setExportYear (year-dropdown crash)
- TeamDashboard: drop redundant setState-in-effect (cascading renders)
- Companies: delete dead _UnusedClientCompanies (illegal hook calls)
- annotate intentional empty PDF-fallback catches

Load speed:
- preconnect/dns-prefetch to Supabase origin
- lazy-load heic-to in Converters: page chunk 2737KB -> 9KB
- split recharts into its own 'charts' vendor chunk

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Krao Hasanee
2026-06-08 12:59:11 -04:00
parent 85625f4d95
commit 04e0911e9f
101 changed files with 11786 additions and 7445 deletions
+86 -17
View File
@@ -8,6 +8,7 @@ const defaultDeadline = () => addDaysToDateOnly(getTodayDateOnlyEST(), 3);
const modalLabelStyle = { fontSize: 11, fontWeight: 500, color: 'var(--text-secondary)', textTransform: 'uppercase', letterSpacing: 0.8, marginBottom: 6 };
const modalInputStyle = { fontSize: 13, padding: '0 5px', textAlign: 'left', lineHeight: 1 };
const modalTextAreaStyle = { ...modalInputStyle, minHeight: 100, padding: '8px 5px', lineHeight: 1.4 };
const modalActionButtonStyle = { width: 132, justifyContent: 'center', flex: '0 0 132px' };
const emptyForm = (companyId = '') => ({
companyId,
project: '',
@@ -43,7 +44,11 @@ export default function RequestForm({
initialValues = null,
lockedFields = [],
}) {
const [form, setForm] = useState(() => ({ ...emptyForm(initialCompanyId), ...(initialValues || {}) }));
const [form, setForm] = useState(() => ({
...emptyForm(initialCompanyId),
requestedBy: showRequester ? (currentUser?.id || '') : '',
...(initialValues || {}),
}));
const prevCompanyIdRef = useRef(initialValues?.companyId || initialCompanyId || null);
const [files, setFiles] = useState([]);
const [existingProjects, setExistingProjects] = useState([]);
@@ -54,16 +59,17 @@ export default function RequestForm({
const [signFamilies, setSignFamilies] = useState([]);
const [signCount, setSignCount] = useState('');
const [signs, setSigns] = useState([]);
const [localError, setLocalError] = useState('');
const isBrandBook = form.serviceType === 'Brand Book';
const usesSignFields = form.serviceType === 'Brand Book';
useEffect(() => {
supabase.from('sign_families').select('name').order('sort_order').then(({ data }) => setSignFamilies((data || []).map(r => r.name)));
}, []);
useEffect(() => {
if (!isBrandBook) { setSignCount(''); setSigns([]); }
}, [isBrandBook]);
if (!usesSignFields) { setSignCount(''); setSigns([]); }
}, [usesSignFields]);
useEffect(() => {
const n = Math.max(0, parseInt(signCount) || 0);
@@ -113,9 +119,15 @@ export default function RequestForm({
prevCompanyIdRef.current = companyId;
}, [companyId]); // eslint-disable-line react-hooks/exhaustive-deps
const set = (field) => (e) => setForm(f => ({ ...f, [field]: e.target.value }));
const set = (field) => (e) => {
if (localError) setLocalError('');
setForm(f => ({ ...f, [field]: e.target.value }));
};
const setSign = (id, field, value) => setSigns(prev => prev.map(s => s.id === id ? { ...s, [field]: value } : s));
const setSign = (id, field, value) => {
if (localError) setLocalError('');
setSigns(prev => prev.map(s => s.id === id ? { ...s, [field]: value } : s));
};
const allProjectNames = [
...existingProjects.map(p => p.name),
@@ -143,18 +155,56 @@ export default function RequestForm({
};
const showCompanySelect = companies.length > 1 || showRequester;
const requesterOptions = [
...(currentUser ? [{ id: currentUser.id, name: `${currentUser.name} (You)` }] : []),
...companyUsers.filter(u => u.id !== currentUser?.id),
];
const handleSubmit = (e) => {
e.preventDefault();
const requestedByName = currentUser?.name || '';
setLocalError('');
const selectedRequester = showRequester
? requesterOptions.find(option => option.id === form.requestedBy) || null
: null;
const requestedBy = showRequester
? (selectedRequester?.id || '')
: (currentUser?.id || '');
const requestedByName = showRequester
? ((selectedRequester?.name || '').replace(/\s+\(You\)$/, '') || '')
: (currentUser?.name || '');
let normalizedSignCount = null;
let normalizedSigns = [];
if (usesSignFields) {
normalizedSignCount = parseInt(signCount, 10) || null;
const formData = new FormData(e.currentTarget);
normalizedSigns = Array.from({ length: normalizedSignCount || 0 }, (_, index) => {
const stateSign = signs[index] || {};
const domValue = formData.get(`sign_info_${index + 1}`);
const signName = typeof domValue === 'string'
? domValue.trim()
: String(stateSign.signName || '').trim();
return {
id: stateSign.id || `sign-${index + 1}`,
signName,
signFamily: stateSign.signFamily || '',
};
}).filter(sign => sign.signName || sign.signFamily);
if ((normalizedSignCount || 0) > 0 && normalizedSigns.length !== normalizedSignCount) {
setLocalError('Please fill in the sign information for each sign before submitting.');
return;
}
}
onSubmit(
{
...form,
companyId,
requestedBy: currentUser?.id || '',
requestedBy,
requestedByName,
signCount: isBrandBook ? (parseInt(signCount) || null) : null,
signs: isBrandBook ? signs : [],
signCount: usesSignFields ? normalizedSignCount : null,
signs: usesSignFields ? normalizedSigns : [],
},
files,
existingProjects
@@ -214,6 +264,24 @@ export default function RequestForm({
</div>
</div>
{showRequester && (
<div className="form-group">
<label style={modalLabelStyle}>Requested By *</label>
<select
value={form.requestedBy}
onChange={set('requestedBy')}
required
disabled={!companyId}
style={modalInputStyle}
>
<option value="">{companyId ? 'Select requester...' : 'Select company first'}</option>
{requesterOptions.map(user => (
<option key={user.id} value={user.id}>{user.name}</option>
))}
</select>
</div>
)}
{/* Row 3: Title/Location + Mark as Hot inline */}
<div style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: 12, alignItems: 'end', marginBottom: 16 }}>
<div className="form-group" style={{ marginBottom: 0 }}>
@@ -226,7 +294,7 @@ export default function RequestForm({
</label>
</div>
{isBrandBook && (
{usesSignFields && (
<div className="form-group">
<label style={modalLabelStyle}>Sign Count *</label>
<input
@@ -242,7 +310,7 @@ export default function RequestForm({
</div>
)}
{isBrandBook && signs.length > 0 && (
{usesSignFields && signs.length > 0 && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 16 }}>
{signs.map((sign, i) => (
<div key={sign.id} style={{ border: '1px solid var(--border)', borderRadius: 6, padding: '10px 12px' }}>
@@ -251,6 +319,7 @@ export default function RequestForm({
<label style={modalLabelStyle}>Sign Information *</label>
<input
type="text"
name={`sign_info_${i + 1}`}
placeholder="e.g. Main Entry, Drive Thru"
value={sign.signName}
onChange={e => setSign(sign.id, 'signName', e.target.value)}
@@ -264,19 +333,19 @@ export default function RequestForm({
)}
<div className="form-group">
<label style={modalLabelStyle}>{isBrandBook ? 'Notes' : 'Description'} *</label>
<label style={modalLabelStyle}>{usesSignFields ? 'Notes' : 'Description'} *</label>
<textarea placeholder="Notes on the request..." value={form.description} onChange={set('description')} style={modalTextAreaStyle} required />
</div>
<FileAttachment files={files} onChange={setFiles} />
{error && <div style={{ fontSize: 13, color: 'var(--danger)', marginBottom: 12 }}> {error}</div>}
{(localError || error) && <div style={{ fontSize: 13, color: 'var(--danger)', marginBottom: 12 }}> {localError || error}</div>}
<div className="action-buttons" style={{ justifyContent: 'flex-end' }}>
<button type="submit" className="btn btn-outline" disabled={saving}>
<div className="action-buttons modal-action-row" style={{ justifyContent: 'flex-end' }}>
<button type="submit" className="btn btn-outline" disabled={saving} style={modalActionButtonStyle}>
{saving ? 'Submitting...' : submitLabel}
</button>
{onCancel && <button type="button" className="btn btn-outline" onClick={onCancel}>Cancel</button>}
{onCancel && <button type="button" className="btn btn-outline" onClick={onCancel} style={modalActionButtonStyle}>Cancel</button>}
</div>
</form>
);