Amazon Shoe Sale Tracker

A new extension

Size

18.8 KB

Version

1.0.1

Created

Jan 28, 2026

Updated

20 days ago

1// ==UserScript==
2// @name		Amazon Shoe Sale Tracker
3// @description		A new extension
4// @version		1.0.1
5// @match		https://*.amazon.com/*
6// @icon		https://www.amazon.com/favicon.ico
7// @grant		GM.getValue
8// @grant		GM.setValue
9// @grant		GM.deleteValue
10// ==/UserScript==
11(function() {
12    'use strict';
13
14    console.log('Amazon Shoe Sale Tracker initialized');
15
16    // Debounce function to prevent excessive calls
17    function debounce(func, wait) {
18        let timeout;
19        return function executedFunction(...args) {
20            const later = () => {
21                clearTimeout(timeout);
22                func(...args);
23            };
24            clearTimeout(timeout);
25            timeout = setTimeout(later, wait);
26        };
27    }
28
29    // Function to extract product data from a product item
30    function extractProductData(productItem) {
31        try {
32            const titleElement = productItem.querySelector('a[data-testid="product-grid-title"]');
33            const title = titleElement ? titleElement.textContent.trim() : 'Unknown Product';
34            
35            const priceDiv = productItem.querySelector('div[data-testid="grid-item-buy-price"]');
36            if (!priceDiv) return null;
37
38            // Check for strikethrough price (indicates sale)
39            const strikeThroughPrice = priceDiv.querySelector('.StrikeThroughPrice__strikePrice__stBvh');
40            if (!strikeThroughPrice) return null; // Not on sale
41
42            const buyPriceElement = priceDiv.querySelector('span[data-testid^="$"]');
43            const buyPrice = buyPriceElement ? buyPriceElement.getAttribute('data-testid') : 'N/A';
44            const originalPrice = strikeThroughPrice.getAttribute('data-testid') || 'N/A';
45
46            // Get product link
47            const linkElement = productItem.querySelector('a[href*="/dp/"]');
48            const productUrl = linkElement ? 'https://www.amazon.com' + linkElement.getAttribute('href') : '';
49
50            // Get image
51            const imgElement = productItem.querySelector('img[data-testid="image"]');
52            const imageUrl = imgElement ? imgElement.src : '';
53
54            // Get brand
55            const brandElement = productItem.querySelector('div[data-testid="grid-item-info"] > div > div:first-child');
56            const brand = brandElement ? brandElement.textContent.trim() : '';
57
58            // Calculate discount percentage
59            const buyPriceNum = parseFloat(buyPrice.replace('$', ''));
60            const originalPriceNum = parseFloat(originalPrice.replace('$', ''));
61            const discountPercent = originalPriceNum > 0 ? Math.round(((originalPriceNum - buyPriceNum) / originalPriceNum) * 100) : 0;
62
63            return {
64                title,
65                brand,
66                buyPrice,
67                originalPrice,
68                discountPercent,
69                productUrl,
70                imageUrl,
71                timestamp: Date.now(),
72                id: productUrl.match(/\/dp\/([A-Z0-9]+)/)?.[1] || Date.now().toString()
73            };
74        } catch (error) {
75            console.error('Error extracting product data:', error);
76            return null;
77        }
78    }
79
80    // Function to highlight sale items
81    async function highlightSaleItems() {
82        console.log('Scanning for sale items...');
83        const productItems = document.querySelectorAll('li[data-testid="product-grid-item"]');
84        console.log(`Found ${productItems.length} product items`);
85
86        let saleCount = 0;
87        const saleProducts = [];
88
89        productItems.forEach(item => {
90            const priceDiv = item.querySelector('div[data-testid="grid-item-buy-price"]');
91            if (!priceDiv) return;
92
93            const strikeThroughPrice = priceDiv.querySelector('.StrikeThroughPrice__strikePrice__stBvh');
94            if (strikeThroughPrice) {
95                // This item is on sale
96                saleCount++;
97
98                // Add highlight border
99                if (!item.classList.contains('sale-highlighted')) {
100                    item.style.border = '3px solid #ff6b6b';
101                    item.style.borderRadius = '8px';
102                    item.style.boxShadow = '0 0 15px rgba(255, 107, 107, 0.5)';
103                    item.style.position = 'relative';
104                    item.classList.add('sale-highlighted');
105
106                    // Add sale badge
107                    const badge = document.createElement('div');
108                    badge.className = 'sale-badge';
109                    badge.textContent = '🔥 ON SALE';
110                    badge.style.cssText = `
111                        position: absolute;
112                        top: 10px;
113                        right: 10px;
114                        background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
115                        color: white;
116                        padding: 8px 15px;
117                        border-radius: 20px;
118                        font-weight: bold;
119                        font-size: 12px;
120                        z-index: 10;
121                        box-shadow: 0 2px 8px rgba(0,0,0,0.2);
122                        letter-spacing: 0.5px;
123                    `;
124                    item.style.position = 'relative';
125                    item.insertBefore(badge, item.firstChild);
126
127                    // Extract and save product data
128                    const productData = extractProductData(item);
129                    if (productData) {
130                        saleProducts.push(productData);
131                        console.log('Found sale item:', productData.title, productData.buyPrice, 'was', productData.originalPrice);
132                    }
133                }
134            }
135        });
136
137        // Save sale products to database
138        if (saleProducts.length > 0) {
139            await saveSaleProducts(saleProducts);
140        }
141
142        console.log(`Highlighted ${saleCount} items on sale`);
143    }
144
145    // Save sale products to database
146    async function saveSaleProducts(newProducts) {
147        try {
148            const existingData = await GM.getValue('saleProducts', '[]');
149            const existingProducts = JSON.parse(existingData);
150            
151            // Create a map of existing products by ID
152            const productMap = new Map();
153            existingProducts.forEach(p => productMap.set(p.id, p));
154            
155            // Add or update products
156            newProducts.forEach(p => {
157                productMap.set(p.id, p);
158            });
159            
160            // Convert back to array and save
161            const updatedProducts = Array.from(productMap.values());
162            await GM.setValue('saleProducts', JSON.stringify(updatedProducts));
163            console.log(`Saved ${newProducts.length} new sale products. Total: ${updatedProducts.length}`);
164            
165            // Update badge count
166            updateBadgeCount(updatedProducts.length);
167        } catch (error) {
168            console.error('Error saving sale products:', error);
169        }
170    }
171
172    // Update badge count on floating button
173    function updateBadgeCount(count) {
174        const badge = document.getElementById('sale-tracker-badge');
175        if (badge) {
176            badge.textContent = count;
177        }
178    }
179
180    // Create floating button to view saved sales
181    function createFloatingButton() {
182        const button = document.createElement('button');
183        button.id = 'sale-tracker-button';
184        button.innerHTML = `
185            <span style="font-size: 24px;">👟</span>
186            <span style="font-size: 11px; margin-top: 2px;">Sale Tracker</span>
187            <span id="sale-tracker-badge" style="
188                position: absolute;
189                top: -5px;
190                right: -5px;
191                background: #ff6b6b;
192                color: white;
193                border-radius: 50%;
194                width: 24px;
195                height: 24px;
196                display: flex;
197                align-items: center;
198                justify-content: center;
199                font-size: 11px;
200                font-weight: bold;
201                border: 2px solid white;
202            ">0</span>
203        `;
204        button.style.cssText = `
205            position: fixed;
206            bottom: 30px;
207            right: 30px;
208            width: 80px;
209            height: 80px;
210            border-radius: 50%;
211            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
212            color: white;
213            border: none;
214            cursor: pointer;
215            box-shadow: 0 4px 15px rgba(0,0,0,0.3);
216            z-index: 10000;
217            display: flex;
218            flex-direction: column;
219            align-items: center;
220            justify-content: center;
221            font-weight: bold;
222            transition: transform 0.2s, box-shadow 0.2s;
223        `;
224
225        button.addEventListener('mouseenter', () => {
226            button.style.transform = 'scale(1.1)';
227            button.style.boxShadow = '0 6px 20px rgba(0,0,0,0.4)';
228        });
229
230        button.addEventListener('mouseleave', () => {
231            button.style.transform = 'scale(1)';
232            button.style.boxShadow = '0 4px 15px rgba(0,0,0,0.3)';
233        });
234
235        button.addEventListener('click', showSalePanel);
236        document.body.appendChild(button);
237
238        // Load initial count
239        loadBadgeCount();
240    }
241
242    // Load badge count
243    async function loadBadgeCount() {
244        try {
245            const existingData = await GM.getValue('saleProducts', '[]');
246            const products = JSON.parse(existingData);
247            updateBadgeCount(products.length);
248        } catch (error) {
249            console.error('Error loading badge count:', error);
250        }
251    }
252
253    // Show sale panel
254    async function showSalePanel() {
255        console.log('Opening sale panel...');
256        
257        // Check if panel already exists
258        let panel = document.getElementById('sale-tracker-panel');
259        if (panel) {
260            panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
261            return;
262        }
263
264        // Create panel
265        panel = document.createElement('div');
266        panel.id = 'sale-tracker-panel';
267        panel.style.cssText = `
268            position: fixed;
269            top: 50%;
270            left: 50%;
271            transform: translate(-50%, -50%);
272            width: 90%;
273            max-width: 900px;
274            height: 80vh;
275            background: white;
276            border-radius: 15px;
277            box-shadow: 0 10px 40px rgba(0,0,0,0.3);
278            z-index: 10001;
279            display: flex;
280            flex-direction: column;
281            overflow: hidden;
282        `;
283
284        // Header
285        const header = document.createElement('div');
286        header.style.cssText = `
287            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
288            color: white;
289            padding: 20px;
290            display: flex;
291            justify-content: space-between;
292            align-items: center;
293        `;
294        header.innerHTML = `
295            <h2 style="margin: 0; font-size: 24px;">👟 Saved Sale Items</h2>
296            <div>
297                <button id="clear-sales-btn" style="
298                    background: rgba(255,255,255,0.2);
299                    color: white;
300                    border: 1px solid white;
301                    padding: 8px 15px;
302                    border-radius: 5px;
303                    cursor: pointer;
304                    margin-right: 10px;
305                    font-weight: bold;
306                ">Clear All</button>
307                <button id="close-panel-btn" style="
308                    background: transparent;
309                    color: white;
310                    border: none;
311                    font-size: 30px;
312                    cursor: pointer;
313                    padding: 0;
314                    width: 30px;
315                    height: 30px;
316                    line-height: 30px;
317                ">×</button>
318            </div>
319        `;
320
321        // Content area
322        const content = document.createElement('div');
323        content.id = 'sale-panel-content';
324        content.style.cssText = `
325            flex: 1;
326            overflow-y: auto;
327            padding: 20px;
328            background: #f5f5f5;
329        `;
330
331        panel.appendChild(header);
332        panel.appendChild(content);
333        document.body.appendChild(panel);
334
335        // Load and display products
336        await loadSaleProducts();
337
338        // Event listeners
339        document.getElementById('close-panel-btn').addEventListener('click', () => {
340            panel.style.display = 'none';
341        });
342
343        document.getElementById('clear-sales-btn').addEventListener('click', async () => {
344            if (confirm('Are you sure you want to clear all saved sale items?')) {
345                await GM.setValue('saleProducts', '[]');
346                updateBadgeCount(0);
347                await loadSaleProducts();
348            }
349        });
350    }
351
352    // Load and display sale products
353    async function loadSaleProducts() {
354        try {
355            const existingData = await GM.getValue('saleProducts', '[]');
356            const products = JSON.parse(existingData);
357            const content = document.getElementById('sale-panel-content');
358
359            if (products.length === 0) {
360                content.innerHTML = `
361                    <div style="
362                        text-align: center;
363                        padding: 60px 20px;
364                        color: #666;
365                    ">
366                        <div style="font-size: 60px; margin-bottom: 20px;">👟</div>
367                        <h3 style="margin: 0 0 10px 0;">No sale items saved yet</h3>
368                        <p style="margin: 0;">Browse Amazon shoes and sale items will be automatically saved here!</p>
369                    </div>
370                `;
371                return;
372            }
373
374            // Sort by discount percentage (highest first)
375            products.sort((a, b) => b.discountPercent - a.discountPercent);
376
377            content.innerHTML = `
378                <div style="
379                    display: grid;
380                    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
381                    gap: 20px;
382                ">
383                    ${products.map(product => `
384                        <div style="
385                            background: white;
386                            border-radius: 10px;
387                            padding: 15px;
388                            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
389                            transition: transform 0.2s;
390                            cursor: pointer;
391                        " onmouseover="this.style.transform='translateY(-5px)'; this.style.boxShadow='0 4px 15px rgba(0,0,0,0.2)'" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 8px rgba(0,0,0,0.1)'" onclick="window.open('${product.productUrl}', '_blank')">
392                            <div style="
393                                width: 100%;
394                                height: 200px;
395                                background: #f9f9f9;
396                                border-radius: 8px;
397                                margin-bottom: 12px;
398                                display: flex;
399                                align-items: center;
400                                justify-content: center;
401                                overflow: hidden;
402                            ">
403                                ${product.imageUrl ? `<img src="${product.imageUrl}" style="max-width: 100%; max-height: 100%; object-fit: contain;">` : '<div style="color: #ccc; font-size: 40px;">👟</div>'}
404                            </div>
405                            <div style="
406                                position: absolute;
407                                top: 25px;
408                                right: 25px;
409                                background: #ff6b6b;
410                                color: white;
411                                padding: 5px 10px;
412                                border-radius: 15px;
413                                font-weight: bold;
414                                font-size: 12px;
415                            ">-${product.discountPercent}%</div>
416                            <div style="color: #888; font-size: 12px; margin-bottom: 5px;">${product.brand}</div>
417                            <div style="
418                                font-weight: bold;
419                                margin-bottom: 10px;
420                                font-size: 14px;
421                                line-height: 1.4;
422                                height: 40px;
423                                overflow: hidden;
424                                display: -webkit-box;
425                                -webkit-line-clamp: 2;
426                                -webkit-box-orient: vertical;
427                            ">${product.title}</div>
428                            <div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
429                                <span style="
430                                    font-size: 20px;
431                                    font-weight: bold;
432                                    color: #ff6b6b;
433                                ">${product.buyPrice}</span>
434                                <span style="
435                                    text-decoration: line-through;
436                                    color: #999;
437                                    font-size: 14px;
438                                ">${product.originalPrice}</span>
439                            </div>
440                            <div style="
441                                font-size: 11px;
442                                color: #999;
443                            ">Saved ${new Date(product.timestamp).toLocaleDateString()}</div>
444                        </div>
445                    `).join('')}
446                </div>
447            `;
448        } catch (error) {
449            console.error('Error loading sale products:', error);
450        }
451    }
452
453    // Initialize
454    function init() {
455        console.log('Initializing Amazon Shoe Sale Tracker...');
456        
457        // Create floating button
458        createFloatingButton();
459
460        // Initial scan
461        setTimeout(() => {
462            highlightSaleItems();
463        }, 2000);
464
465        // Watch for DOM changes (new products loaded on scroll)
466        const debouncedHighlight = debounce(highlightSaleItems, 1000);
467        const observer = new MutationObserver(debouncedHighlight);
468        
469        // Observe the product grid
470        const observeProductGrid = () => {
471            const productGrid = document.querySelector('[data-testid="product-grid-container"]');
472            if (productGrid) {
473                observer.observe(productGrid, {
474                    childList: true,
475                    subtree: true
476                });
477                console.log('Observing product grid for changes');
478            } else {
479                setTimeout(observeProductGrid, 1000);
480            }
481        };
482        
483        observeProductGrid();
484    }
485
486    // Start when page is ready
487    if (document.readyState === 'loading') {
488        document.addEventListener('DOMContentLoaded', init);
489    } else {
490        init();
491    }
492
493})();
Amazon Shoe Sale Tracker | Robomonkey