/** * GoingsOn - External File Import Module * Handles importing contacts from vCard (.vcf) and events from iCalendar (.ics) files. */ (function() { 'use strict'; const esc = GoingsOn.utils.escapeHtml; // ============ Import Type Selection ============ /** * Opens the external import dialog with type selection. */ function openImportDialog() { const content = `
No contacts found in file.
', null); return; } const maxPreview = 25; const display = preview.slice(0, maxPreview); const html = `${preview.length} contact${preview.length !== 1 ? 's' : ''} found
| Name | Company | Emails | Phones |
|---|---|---|---|
| ${esc(c.displayName)} | ${esc(c.company || '')} | ${c.emailCount} | ${c.phoneCount} |
...and ${preview.length - maxPreview} more
` : ''} `; updatePreviewContent(html, async () => { await executeContactImport(filePath, preview.length); }); } catch (err) { updatePreviewContent(`Failed to parse file: ${esc(GoingsOn.utils.getErrorMessage(err))}
`, null); } } /** * Executes the vCard import. */ async function executeContactImport(filePath, expectedCount) { const btn = document.getElementById('import-external-confirm'); if (btn) { btn.disabled = true; btn.textContent = 'Importing...'; } try { const result = await GoingsOn.api.import.importVcf(filePath); GoingsOn.ui.closeModal(); const parts = []; if (result.imported > 0) parts.push(`${result.imported} imported`); if (result.skipped > 0) parts.push(`${result.skipped} skipped (duplicates)`); if (result.errors.length > 0) parts.push(`${result.errors.length} failed`); const hasErrors = result.errors.length > 0; GoingsOn.ui.showToast( `Contacts: ${parts.join(', ')}`, hasErrors ? 'warning' : 'success' ); if (GoingsOn.navigation && GoingsOn.navigation.reloadCurrentView) { GoingsOn.navigation.reloadCurrentView(); } } catch (err) { GoingsOn.ui.showToast('Import failed: ' + GoingsOn.utils.getErrorMessage(err), 'error'); if (btn) { btn.disabled = false; btn.textContent = 'Import'; } } } // ============ Calendar Import ============ /** * Opens file picker for ICS import, then shows preview. */ async function startCalendarImport() { const filePath = await pickFile('iCalendar Files', ['ics']); if (!filePath) return; GoingsOn.ui.openModal('Import Calendar', `No events found in file.
', null); return; } const maxPreview = 25; const display = preview.slice(0, maxPreview); const html = `${preview.length} event${preview.length !== 1 ? 's' : ''} found
| Title | Start | Location | Recurrence |
|---|---|---|---|
| ${esc(e.title)} | ${esc(formatImportDate(e.startTime))} | ${esc(e.location || '')} | ${esc(e.recurrence)} |
...and ${preview.length - maxPreview} more
` : ''} `; updatePreviewContent(html, async () => { await executeCalendarImport(filePath, preview.length); }); } catch (err) { updatePreviewContent(`Failed to parse file: ${esc(GoingsOn.utils.getErrorMessage(err))}
`, null); } } /** * Executes the ICS import. */ async function executeCalendarImport(filePath, expectedCount) { const btn = document.getElementById('import-external-confirm'); if (btn) { btn.disabled = true; btn.textContent = 'Importing...'; } try { const result = await GoingsOn.api.import.importIcs(filePath); GoingsOn.ui.closeModal(); const parts = []; if (result.imported > 0) parts.push(`${result.imported} imported`); if (result.skipped > 0) parts.push(`${result.skipped} skipped (duplicates)`); if (result.errors.length > 0) parts.push(`${result.errors.length} failed`); const hasErrors = result.errors.length > 0; GoingsOn.ui.showToast( `Events: ${parts.join(', ')}`, hasErrors ? 'warning' : 'success' ); if (GoingsOn.navigation && GoingsOn.navigation.reloadCurrentView) { GoingsOn.navigation.reloadCurrentView(); } } catch (err) { GoingsOn.ui.showToast('Import failed: ' + GoingsOn.utils.getErrorMessage(err), 'error'); if (btn) { btn.disabled = false; btn.textContent = 'Import'; } } } // ============ Helpers ============ /** * Opens native file picker with given filter. * @param {string} filterName - Display name for the file filter * @param {string[]} extensions - Allowed file extensions * @returns {Promise