Success.ai Lead Data Exporter

A new extension

Size

16.4 KB

Version

1.1.1

Created

Dec 17, 2025

Updated

about 2 months ago

1// ==UserScript==
2// @name		Success.ai Lead Data Exporter
3// @description		A new extension
4// @version		1.1.1
5// @match		https://*.app.success.ai/*
6// @icon		https://app.success.ai/MainFavIcon.png
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    console.log('Success.ai Lead Data Exporter initialized');
12
13    let isExporting = false;
14    let allLeadsData = [];
15
16    // Utility function to wait for elements
17    function waitForElement(selector, timeout = 10000) {
18        return new Promise((resolve, reject) => {
19            if (document.querySelector(selector)) {
20                return resolve(document.querySelector(selector));
21            }
22
23            const observer = new MutationObserver(() => {
24                if (document.querySelector(selector)) {
25                    observer.disconnect();
26                    resolve(document.querySelector(selector));
27                }
28            });
29
30            observer.observe(document.body, {
31                childList: true,
32                subtree: true
33            });
34
35            setTimeout(() => {
36                observer.disconnect();
37                reject(new Error(`Element ${selector} not found within ${timeout}ms`));
38            }, timeout);
39        });
40    }
41
42    // Function to extract data from current page
43    function extractCurrentPageData() {
44        console.log('Extracting data from current page...');
45        const leads = [];
46        
47        // Find all rows in the data grid
48        const rows = document.querySelectorAll('.MuiDataGrid-row');
49        console.log(`Found ${rows.length} rows on current page`);
50        
51        rows.forEach((row, index) => {
52            try {
53                const cells = row.querySelectorAll('.MuiDataGrid-cell');
54                const leadData = {};
55                
56                cells.forEach(cell => {
57                    const field = cell.getAttribute('data-field');
58                    if (field && field !== '__check__') {
59                        const value = cell.textContent.trim();
60                        leadData[field] = value;
61                    }
62                });
63                
64                // Only add if we have actual data
65                if (Object.keys(leadData).length > 0) {
66                    leads.push(leadData);
67                    console.log(`Extracted lead ${index + 1}:`, leadData);
68                }
69            } catch (error) {
70                console.error(`Error extracting row ${index}:`, error);
71            }
72        });
73        
74        return leads;
75    }
76
77    // Function to set rows per page to 100
78    async function setRowsPerPage(rowCount) {
79        console.log(`Setting rows per page to ${rowCount}...`);
80        
81        try {
82            // Find the rows per page dropdown
83            const dropdown = document.querySelector('.MuiSelect-select.MuiSelect-standard');
84            
85            if (!dropdown) {
86                console.error('Rows per page dropdown not found');
87                return false;
88            }
89            
90            // Click to open dropdown
91            dropdown.click();
92            
93            // Wait for menu to appear
94            await new Promise(resolve => setTimeout(resolve, 500));
95            
96            // Find the option with value 100
97            const options = document.querySelectorAll('[role="option"]');
98            let targetOption = null;
99            
100            for (let option of options) {
101                if (option.textContent.trim() === String(rowCount)) {
102                    targetOption = option;
103                    break;
104                }
105            }
106            
107            if (targetOption) {
108                console.log(`Clicking option: ${rowCount}`);
109                targetOption.click();
110                
111                // Wait for the page to reload with new row count
112                await new Promise(resolve => setTimeout(resolve, 3000));
113                
114                // Wait for data grid to update
115                await waitForElement('.MuiDataGrid-row', 5000);
116                
117                console.log(`Successfully set rows per page to ${rowCount}`);
118                return true;
119            } else {
120                console.error(`Option ${rowCount} not found in dropdown`);
121                // Close the dropdown if we couldn't find the option
122                dropdown.click();
123                return false;
124            }
125        } catch (error) {
126            console.error('Error setting rows per page:', error);
127            return false;
128        }
129    }
130
131    // Function to check if there's a next page
132    function hasNextPage() {
133        // Find the next page button (the second button in pagination that's not disabled)
134        const paginationButtons = document.querySelectorAll('.MuiButton-root.MuiButton-text.MuiButton-textPrimary');
135        
136        for (let button of paginationButtons) {
137            const startIcon = button.querySelector('.MuiButton-startIcon');
138            if (startIcon && !button.classList.contains('Mui-disabled')) {
139                // Check if this is the "next" button (usually has an arrow icon)
140                const parentDiv = button.closest('div');
141                if (parentDiv && parentDiv.previousElementSibling) {
142                    console.log('Next page button found and enabled');
143                    return true;
144                }
145            }
146        }
147        console.log('No next page available');
148        return false;
149    }
150
151    // Function to click next page
152    async function goToNextPage() {
153        console.log('Attempting to go to next page...');
154        
155        // Find the next page button
156        const paginationButtons = document.querySelectorAll('.MuiButton-root.MuiButton-text.MuiButton-textPrimary');
157        
158        for (let button of paginationButtons) {
159            const startIcon = button.querySelector('.MuiButton-startIcon');
160            if (startIcon && !button.classList.contains('Mui-disabled')) {
161                const parentDiv = button.closest('div');
162                if (parentDiv && parentDiv.previousElementSibling) {
163                    console.log('Clicking next page button...');
164                    button.click();
165                    
166                    // Wait for the page to load
167                    await new Promise(resolve => setTimeout(resolve, 3000));
168                    
169                    // Wait for the data grid to update
170                    await waitForElement('.MuiDataGrid-row', 5000);
171                    
172                    return true;
173                }
174            }
175        }
176        
177        return false;
178    }
179
180    // Function to convert data to CSV
181    function convertToCSV(data) {
182        if (data.length === 0) {
183            return '';
184        }
185
186        // Get all unique headers
187        const headers = new Set();
188        data.forEach(row => {
189            Object.keys(row).forEach(key => headers.add(key));
190        });
191        const headerArray = Array.from(headers);
192
193        // Create CSV header row
194        const csvRows = [];
195        csvRows.push(headerArray.map(h => `"${h}"`).join(','));
196
197        // Create CSV data rows
198        data.forEach(row => {
199            const values = headerArray.map(header => {
200                const value = row[header] || '';
201                return `"${String(value).replace(/"/g, '""')}"`;
202            });
203            csvRows.push(values.join(','));
204        });
205
206        return csvRows.join('\n');
207    }
208
209    // Function to download CSV
210    function downloadCSV(csvContent, filename) {
211        const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
212        const link = document.createElement('a');
213        const url = URL.createObjectURL(blob);
214        
215        link.setAttribute('href', url);
216        link.setAttribute('download', filename);
217        link.style.visibility = 'hidden';
218        
219        document.body.appendChild(link);
220        link.click();
221        document.body.removeChild(link);
222        
223        console.log(`CSV downloaded: ${filename}`);
224    }
225
226    // Main export function
227    async function exportAllData() {
228        if (isExporting) {
229            console.log('Export already in progress');
230            return;
231        }
232
233        isExporting = true;
234        allLeadsData = [];
235        
236        const exportButton = document.getElementById('export-leads-button');
237        const originalText = exportButton.textContent;
238        
239        try {
240            exportButton.textContent = 'Preparing...';
241            exportButton.disabled = true;
242            exportButton.style.opacity = '0.6';
243            exportButton.style.cursor = 'not-allowed';
244            
245            // First, set rows per page to 100
246            console.log('Setting rows per page to 100...');
247            const rowsSet = await setRowsPerPage(100);
248            
249            if (!rowsSet) {
250                console.warn('Could not set rows per page to 100, continuing with current setting');
251            }
252            
253            exportButton.textContent = 'Exporting...';
254            
255            let pageCount = 0;
256            let hasMore = true;
257            
258            console.log('Starting export process...');
259            
260            // Extract first page
261            pageCount++;
262            console.log(`Processing page ${pageCount}...`);
263            const firstPageData = extractCurrentPageData();
264            allLeadsData.push(...firstPageData);
265            
266            exportButton.textContent = `Exporting... (Page ${pageCount}, ${allLeadsData.length} leads)`;
267            
268            // Continue to next pages until we reach the end
269            while (hasMore) {
270                hasMore = hasNextPage();
271                
272                if (hasMore) {
273                    const success = await goToNextPage();
274                    
275                    if (success) {
276                        pageCount++;
277                        console.log(`Processing page ${pageCount}...`);
278                        
279                        const pageData = extractCurrentPageData();
280                        allLeadsData.push(...pageData);
281                        
282                        exportButton.textContent = `Exporting... (Page ${pageCount}, ${allLeadsData.length} leads)`;
283                        console.log(`Total leads collected so far: ${allLeadsData.length}`);
284                    } else {
285                        console.log('Could not navigate to next page, stopping');
286                        hasMore = false;
287                    }
288                } else {
289                    console.log('Reached the end of all pages');
290                }
291            }
292            
293            console.log(`Export complete! Total pages: ${pageCount}, Total leads: ${allLeadsData.length}`);
294            
295            // Only download CSV after all pages are scraped
296            if (allLeadsData.length > 0) {
297                exportButton.textContent = 'Generating CSV...';
298                
299                const csvContent = convertToCSV(allLeadsData);
300                const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
301                const filename = `success-ai-leads-${timestamp}.csv`;
302                
303                downloadCSV(csvContent, filename);
304                
305                exportButton.textContent = `✓ Exported ${allLeadsData.length} leads`;
306                
307                setTimeout(() => {
308                    exportButton.textContent = originalText;
309                    exportButton.disabled = false;
310                    exportButton.style.opacity = '1';
311                    exportButton.style.cursor = 'pointer';
312                }, 3000);
313            } else {
314                console.error('No data found to export');
315                exportButton.textContent = 'No data found';
316                
317                setTimeout(() => {
318                    exportButton.textContent = originalText;
319                    exportButton.disabled = false;
320                    exportButton.style.opacity = '1';
321                    exportButton.style.cursor = 'pointer';
322                }, 3000);
323            }
324            
325        } catch (error) {
326            console.error('Error during export:', error);
327            exportButton.textContent = 'Export failed';
328            
329            setTimeout(() => {
330                exportButton.textContent = originalText;
331                exportButton.disabled = false;
332                exportButton.style.opacity = '1';
333                exportButton.style.cursor = 'pointer';
334            }, 3000);
335        } finally {
336            isExporting = false;
337        }
338    }
339
340    // Function to create and add the export button
341    function createExportButton() {
342        console.log('Creating export button...');
343        
344        // Check if button already exists
345        if (document.getElementById('export-leads-button')) {
346            console.log('Export button already exists');
347            return;
348        }
349        
350        // Find the container where we want to add the button
351        // Looking for the area near the tabs or pagination
352        const targetContainer = document.querySelector('.css-1hdifnl, .css-6xnj6o, .css-aqj51v');
353        
354        if (!targetContainer) {
355            console.error('Could not find target container for export button');
356            return;
357        }
358        
359        // Create button container
360        const buttonContainer = document.createElement('div');
361        buttonContainer.style.cssText = `
362            display: flex;
363            align-items: center;
364            justify-content: flex-end;
365            padding: 12px 16px;
366            margin-bottom: 8px;
367        `;
368        
369        // Create the export button
370        const exportButton = document.createElement('button');
371        exportButton.id = 'export-leads-button';
372        exportButton.textContent = 'Export Data';
373        exportButton.className = 'MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary';
374        exportButton.style.cssText = `
375            background-color: #1976d2;
376            color: white;
377            padding: 8px 22px;
378            border: none;
379            border-radius: 4px;
380            font-size: 14px;
381            font-weight: 500;
382            cursor: pointer;
383            text-transform: uppercase;
384            letter-spacing: 0.02857em;
385            box-shadow: 0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12);
386            transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
387            font-family: "Roboto", "Helvetica", "Arial", sans-serif;
388        `;
389        
390        // Add hover effect
391        exportButton.addEventListener('mouseenter', () => {
392            if (!exportButton.disabled) {
393                exportButton.style.backgroundColor = '#1565c0';
394                exportButton.style.boxShadow = '0px 2px 4px -1px rgba(0,0,0,0.2), 0px 4px 5px 0px rgba(0,0,0,0.14), 0px 1px 10px 0px rgba(0,0,0,0.12)';
395            }
396        });
397        
398        exportButton.addEventListener('mouseleave', () => {
399            if (!exportButton.disabled) {
400                exportButton.style.backgroundColor = '#1976d2';
401                exportButton.style.boxShadow = '0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)';
402            }
403        });
404        
405        // Add click event
406        exportButton.addEventListener('click', exportAllData);
407        
408        buttonContainer.appendChild(exportButton);
409        
410        // Insert the button at the top of the container
411        targetContainer.insertBefore(buttonContainer, targetContainer.firstChild);
412        
413        console.log('Export button created and added to page');
414    }
415
416    // Initialize the extension
417    function init() {
418        console.log('Initializing Success.ai Lead Data Exporter...');
419        
420        // Wait for the page to load
421        if (document.readyState === 'loading') {
422            document.addEventListener('DOMContentLoaded', () => {
423                setTimeout(createExportButton, 2000);
424            });
425        } else {
426            setTimeout(createExportButton, 2000);
427        }
428        
429        // Also observe for dynamic content changes
430        const observer = new MutationObserver(() => {
431            if (!document.getElementById('export-leads-button')) {
432                const dataGrid = document.querySelector('.MuiDataGrid-root');
433                if (dataGrid) {
434                    createExportButton();
435                }
436            }
437        });
438        
439        observer.observe(document.body, {
440            childList: true,
441            subtree: true
442        });
443    }
444
445    // Start the extension
446    init();
447})();
Success.ai Lead Data Exporter | Robomonkey