This commit is contained in:
+3
-3
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user