import React, { useState, useRef } from 'react'; import Papa from 'papaparse'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { ScrollArea } from '@/components/ui/scroll-area'; import { HelpCircle, Upload, AlertTriangle, CheckCircle, Loader2, X } from 'lucide-react'; import { supabase } from '@/integrations/supabase/client'; import { useAuth } from '@/contexts/AuthContext'; import { useToast } from '@/hooks/use-toast'; export default function ChecklistCSVImportDialog({ open, onOpenChange, onSuccess }) { const { user } = useAuth(); const { toast } = useToast(); const fileInputRef = useRef(null); const db = supabase; const [file, setFile] = useState(null); const [parsedData, setParsedData] = useState([]); const [error, setError] = useState(null); const [importing, setImporting] = useState(false); const [step, setStep] = useState('upload'); const handleFileChange = (e) => { const selectedFile = e.target.files[0]; if (selectedFile) { if (selectedFile.type !== 'text/csv' && !selectedFile.name.endsWith('.csv')) { setError('Please upload a valid CSV file.'); return; } setFile(selectedFile); parseCSV(selectedFile); } }; const parseCSV = (file) => { setError(null); Papa.parse(file, { header: true, skipEmptyLines: true, complete: (results) => { if (results.errors.length > 0) { setError(`Error parsing CSV: ${results.errors[0].message}`); return; } const headers = results.meta.fields; const requiredHeaders = ['Checklist Name', 'Items']; const missingHeaders = requiredHeaders.filter(h => !headers.includes(h)); if (missingHeaders.length > 0) { setError(`Missing required columns: ${missingHeaders.join(', ')}. Please check the tooltip for format.`); return; } const processed = results.data.map((row, index) => ({ id: index, title: row['Checklist Name'] || '', itemsRaw: row['Items'] || '', description: row['Description'] || '', valid: !!row['Checklist Name'] && !!row['Items'] })); setParsedData(processed); setStep('preview'); }, error: (err) => { setError(`Failed to read file: ${err.message}`); } }); }; const handleCellEdit = (index, field, value) => { const newData = [...parsedData]; newData[index][field] = value; newData[index].valid = !!newData[index].title && !!newData[index].itemsRaw; setParsedData(newData); }; const removeRow = (index) => { setParsedData(prev => prev.filter((_, i) => i !== index)); }; const handleImport = async () => { const validRows = parsedData.filter(r => r.valid); if (validRows.length === 0) { setError("No valid data to import."); return; } setImporting(true); try { const records = validRows.map(row => { const itemsArray = row.itemsRaw.split('|').map(itemText => ({ text: itemText.trim(), required: false, id: Math.random().toString(36).substr(2, 9) })).filter(i => i.text.length > 0); return { title: row.title, description: row.description, items: itemsArray, created_by: user?.id, }; }); const { error: insertError } = await db .from('checklists') .insert(records); if (insertError) throw insertError; toast({ title: 'Import Successful', description: `Successfully imported ${records.length} checklist templates.`, }); if (onSuccess) onSuccess(); handleClose(); } catch (err) { console.error('Import error:', err); setError(`Database error: ${err.message}`); } finally { setImporting(false); } }; const handleClose = () => { onOpenChange(false); setFile(null); setParsedData([]); setError(null); setStep('upload'); if (fileInputRef.current) fileInputRef.current.value = ''; }; return ( Import Checklists from CSV Upload a CSV file to bulk create checklist templates. {error && ( Error {error} )} {step === 'upload' && (
fileInputRef.current?.click()} >

Click to upload CSV

or drag and drop file here

CSV Format Requirements:

  • Headers must be exactly: Checklist Name, Items, Description
  • Items should be separated by a pipe character (|) e.g. "Task 1|Task 2|Task 3"
  • Description is optional.
Checklist Name,Items,Description
"Weekly Audit","Check Lights|Check Doors|Empty Trash","Weekly facility check"
"Safety Insp","Fire Extinguishers|Exits Clear","Monthly safety"
)} {step === 'preview' && (

Preview Data ({parsedData.length} rows)

Checklist Name * Items (Pipe Separated) * Description {parsedData.map((row, idx) => ( handleCellEdit(idx, 'title', e.target.value)} className="h-8 text-xs" placeholder="Required" /> handleCellEdit(idx, 'itemsRaw', e.target.value)} className="h-8 text-xs" placeholder="Item 1|Item 2..." /> handleCellEdit(idx, 'description', e.target.value)} className="h-8 text-xs" /> ))}
* Required fields {parsedData.filter(r => !r.valid).length} invalid rows will be skipped
)} {step === 'preview' && ( )}
); }