Files: lib/csvParser.ts, lib/xlsxParser.ts
CSV/XLSX parsing runs on main thread, blocking UI for large files (10k+ rows).
Solution:
// Create worker file: lib/parseWorker.ts
self.onmessage = async (e) => {
const { file, type } = e.data;
// Parse in worker
const result = type === 'xlsx' ? await parseXLSX(file) : await parseCSV(file);
self.postMessage(result);
};Effort: Medium (4-6 hours)
Files: components/partner/PartnerList.tsx, components/compare/CompareView.tsx
All list items render to DOM. With 1000+ items, causes severe lag.
Solution: Install @tanstack/react-virtual or react-window
npm install @tanstack/react-virtualimport { useVirtualizer } from '@tanstack/react-virtual';
const virtualizer = useVirtualizer({
count: filtered.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 72, // estimated row height
});Effort: Medium (3-4 hours)
File: lib/dataTransformer.ts:196-213
Building Sets on every import degrades with database size.
Solution:
- Cache existing invite keys in IndexedDB index
- Use bloom filter for quick negative lookups
- Or lazy-load existing data in chunks
Effort: Medium (2-3 hours)
Files: lib/csvParser.ts, lib/xlsxParser.ts, lib/dataTransformer.ts
Three identical ParsedRow interface definitions.
Solution: Move to lib/types.ts and import everywhere.
Effort: Low (30 min)
File: hooks/useTemplates.ts
Hook doesn't expose loading state like usePartners does.
Solution:
const isLoading = templatesQuery === undefined;
return { templates, isLoading, ... };Effort: Low (15 min)
File: store/appStore.ts
Single store with 18+ fields. Could split into:
uiSlice(modals, view mode)filterSlice(search, sort, status)projectSlice(current project)
Effort: Medium (2 hours)
File: lib/columnDetector.ts
Magic numbers scattered throughout:
if (commaDelimitedRatio > 0.2 || ...) // Why 0.2?
if (yesNoRatio > 0.7 || ...) // Why 0.7?Solution: Create config object with named thresholds.
Effort: Low (30 min)
File: components/partner/PartnerCard.tsx:46-51
Clipboard fallback uses deprecated document.execCommand('copy').
Solution: Remove fallback or use clipboard-polyfill package.
Effort: Low (15 min)
File: hooks/useImport.ts
Generic error messages for corrupted files.
Solution: Add specific error types:
class ParseError extends Error {
constructor(message: string, public row?: number, public column?: string) {
super(message);
}
}Effort: Low (1 hour)
File: lib/database.ts
No rollback strategy for failed migrations.
Solution: Add version tracking table and rollback handlers.
Effort: High (4+ hours)
- CSV formula injection protection (Moved to export-time in
lib/utils.ts) - Regex error handling in template pattern matching
- Search input debouncing (300ms)
- Fix useMemo side effects in CompareView