export interface ImportEntry { project_name: string client_name?: string task_name?: string description?: string start_time: string end_time?: string duration: number tags?: string[] } export function parseCSV(text: string): string[][] { const lines = text.split('\n').filter(l => l.trim()) return lines.map(line => { const cells: string[] = [] let current = '' let inQuotes = false for (const char of line) { if (char === '"') { inQuotes = !inQuotes } else if (char === ',' && !inQuotes) { cells.push(current.trim()) current = '' } else { current += char } } cells.push(current.trim()) return cells }) } export function parseDurationString(dur: string): number { // Handle HH:MM:SS, HH:MM, or decimal hours if (dur.includes(':')) { const parts = dur.split(':').map(Number) if (parts.length === 3) return parts[0] * 3600 + parts[1] * 60 + parts[2] if (parts.length === 2) return parts[0] * 3600 + parts[1] * 60 } const hours = parseFloat(dur) if (!isNaN(hours)) return Math.round(hours * 3600) return 0 } export function mapTogglCSV(rows: string[][]): ImportEntry[] { // Toggl CSV format: Description, Project, Client, Task, Tag, Start date, Start time, End date, End time, Duration const header = rows[0].map(h => h.toLowerCase()) const descIdx = header.findIndex(h => h.includes('description')) const projIdx = header.findIndex(h => h.includes('project')) const clientIdx = header.findIndex(h => h.includes('client')) const taskIdx = header.findIndex(h => h.includes('task')) const tagIdx = header.findIndex(h => h.includes('tag')) const startDateIdx = header.findIndex(h => h.includes('start date')) const startTimeIdx = header.findIndex(h => h.includes('start time')) const durationIdx = header.findIndex(h => h.includes('duration')) return rows.slice(1).map(row => ({ project_name: row[projIdx] || 'Imported', client_name: clientIdx >= 0 ? row[clientIdx] : undefined, task_name: taskIdx >= 0 ? row[taskIdx] : undefined, description: descIdx >= 0 ? row[descIdx] : undefined, start_time: combineDateTime(row[startDateIdx], row[startTimeIdx]), duration: durationIdx >= 0 ? parseDurationString(row[durationIdx]) : 0, tags: tagIdx >= 0 && row[tagIdx] ? row[tagIdx].split(',').map(t => t.trim()) : undefined, })) } export function mapGenericCSV(rows: string[][], mapping: Record): ImportEntry[] { return rows.slice(1).map(row => ({ project_name: row[mapping.project] || 'Imported', client_name: mapping.client >= 0 ? row[mapping.client] : undefined, task_name: mapping.task >= 0 ? row[mapping.task] : undefined, description: mapping.description >= 0 ? row[mapping.description] : undefined, start_time: row[mapping.start_time] || new Date().toISOString(), duration: mapping.duration >= 0 ? parseDurationString(row[mapping.duration]) : 0, })) } function combineDateTime(date: string, time: string): string { if (!date) return new Date().toISOString() if (!time) return new Date(date).toISOString() return new Date(`${date} ${time}`).toISOString() }