RJBULK-APP/scripts/screenshot.mjs
SJCoetz 1efa979443 feat: initial setup of rj bulk supplies backoffice app
Adds complete initial codebase for the RJ Bulk Supplies fuel delivery backoffice:
- Authentication pages and protected route layouts
- Customer, supplier, fuel purchase, and delivery note management
- Custom reporting and customer statement sending tools
- Full UI component library and Tailwind styling
- Server actions, API routes, and PocketBase integration
- Development scripts and project configuration updates
- Updated README and package metadata
2026-07-19 20:38:16 +02:00

97 lines
3.6 KiB
JavaScript

/**
* Screenshot the mobile nav open state for QA review.
* Usage: node scripts/screenshot.mjs
*/
import puppeteer from "puppeteer-core";
import { readFileSync } from "fs";
const PB_URL = process.env.POCKETBASE_URL || "http://pocketbase-zinlbpsext2ro0lgb2558vfy.169.239.180.130.sslip.io";
const APP_URL = "http://localhost:4004";
const CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
async function getToken() {
const res = await fetch(`${PB_URL}/api/collections/users/auth-with-password`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ identity: "david@rjbulk.local", password: "ChangeMe123!" }),
});
const data = await res.json();
return data.token;
}
async function shoot(url, name, viewport = { width: 390, height: 844 }, action) {
const browser = await puppeteer.launch({
executablePath: CHROME,
headless: "new",
args: ["--no-sandbox", "--disable-gpu", "--disable-dev-shm-usage"],
});
try {
const page = await browser.newPage();
await page.setViewport(viewport);
await page.setCookie({
name: "pb_auth",
value: await getToken(),
domain: "localhost",
path: "/",
});
await page.goto(`${APP_URL}${url}`, { waitUntil: "networkidle2", timeout: 30000 });
if (action) {
await page.evaluate(action);
await new Promise((r) => setTimeout(r, 500));
}
await page.screenshot({ path: `/tmp/rjbulk-screens/${name}.png`, fullPage: false });
console.log(` ✓ ${name}.png`);
} catch (e) {
console.error(` ✗ ${name}.png — ${e.message}`);
} finally {
await browser.close();
}
}
async function main() {
// Desktop dashboard for reference
await shoot("/dashboard", "10-desktop-dashboard", { width: 1440, height: 900 });
// Mobile dashboard, closed menu
await shoot("/dashboard", "11-mobile-dashboard");
// Mobile menu open
await shoot("/dashboard", "12-mobile-menu-open", { width: 390, height: 844 }, async () => {
const btn = document.querySelector('button[aria-label="Open navigation"]');
if (btn) btn.click();
});
// Mobile menu open, on a form page (where it was "behind forms")
await shoot("/customers/new", "13-mobile-menu-on-form", { width: 390, height: 844 }, async () => {
const btn = document.querySelector('button[aria-label="Open navigation"]');
if (btn) btn.click();
});
// Mobile menu open, on a page with a select (dropdown might be in front)
await shoot("/delivery-notes/new", "14-mobile-menu-on-delivery", { width: 390, height: 844 }, async () => {
const btn = document.querySelector('button[aria-label="Open navigation"]');
if (btn) btn.click();
});
// Mobile menu open, then click a nav item (close behaviour)
await shoot("/dashboard", "15-mobile-menu-then-tap-dashboard", { width: 390, height: 844 }, async () => {
const btn = document.querySelector('button[aria-label="Open navigation"]');
if (btn) btn.click();
await new Promise((r) => setTimeout(r, 300));
const link = document.querySelector('a[href="/dashboard"]');
// Actually click a DIFFERENT link to see if it navigates
const customersLink = Array.from(document.querySelectorAll("a")).find((a) => a.getAttribute("href") === "/customers");
if (customersLink) customersLink.click();
});
// Mobile menu open on settings (which has the Email settings + SMTP password fields)
await shoot("/settings", "16-mobile-menu-on-settings", { width: 390, height: 844 }, async () => {
const btn = document.querySelector('button[aria-label="Open navigation"]');
if (btn) btn.click();
});
}
main().catch((e) => {
console.error(e);
process.exit(1);
});