Scaper fixes - UI website update
Build and Push Docker Image / build (push) Successful in 5m40s

This commit is contained in:
2026-06-06 13:37:18 -05:00
parent 1a83befde3
commit 4db4b9d694
2 changed files with 15 additions and 9 deletions
+3 -3
View File
@@ -42,9 +42,9 @@ The SQLite database is stored on a mounted volume at `/app/data/tracker.db` so i
## Key Features ## Key Features
### Stock Detection ### Stock Detection
Puppeteer navigates to each product URL and waits for React hydration (2.5s delay), then scans all `<span>` elements for exact text matches: Puppeteer navigates to each product URL and waits for React hydration (2.5s delay), then:
- `"Add to Cart"``in_stock` - `<button label="Add to Cart">` present`in_stock`
- `"Sold Out"``sold_out` - Any `<button>` whose text is `"Notify me when available"`, `"Sold Out"`, or `"Out of Stock"``sold_out`
- Neither found → `unknown` - Neither found → `unknown`
Product name is pulled from the `og:title` meta tag (with Ubiquiti store suffix stripped). Thumbnail is pulled from `og:image`. Product name is pulled from the `og:title` meta tag (with Ubiquiti store suffix stripped). Thumbnail is pulled from `og:image`.
+12 -6
View File
@@ -76,12 +76,18 @@ export async function checkStockStatus(url: string): Promise<ScrapeResult> {
const result = await page.evaluate(() => { const result = await page.evaluate(() => {
let status: 'in_stock' | 'sold_out' | 'unknown' = 'unknown'; let status: 'in_stock' | 'sold_out' | 'unknown' = 'unknown';
// Check all spans for the known button text // In stock: primary button carries label="Add to Cart" as an attribute.
const spans = document.querySelectorAll('span'); // Why: styled-components class hashes rebuild every deploy, and "Add to Cart"
for (const span of spans) { // text alone can appear in related-product cards on the page.
const text = span.textContent?.trim(); if (document.querySelector('button[label="Add to Cart"]')) {
if (text === 'Add to Cart') { status = 'in_stock'; break; } status = 'in_stock';
if (text === 'Sold Out') { status = 'sold_out'; break; } } else {
const soldOutPhrases = ['Notify me when available', 'Sold Out', 'Out of Stock'];
const buttons = document.querySelectorAll('button');
for (const btn of buttons) {
const text = btn.textContent?.trim();
if (text && soldOutPhrases.includes(text)) { status = 'sold_out'; break; }
}
} }
// Product name: og:title is most reliable for single-product pages // Product name: og:title is most reliable for single-product pages