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
97 lines
3.6 KiB
JavaScript
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);
|
|
});
|