initial install
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
BIN
teressa-copeland-homes/scripts/debug-no-forms-1773983391865.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 142 KiB |
@@ -13,6 +13,8 @@
|
||||
import { chromium } from 'playwright';
|
||||
import * as fs from 'node:fs/promises';
|
||||
import * as path from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import AdmZip from 'adm-zip';
|
||||
import { config } from 'dotenv';
|
||||
|
||||
config({ path: path.resolve(process.cwd(), '.env.local') });
|
||||
@@ -221,6 +223,17 @@ async function handleNRDSAuth(page: import('playwright').Page) {
|
||||
}
|
||||
}
|
||||
|
||||
function extractFormNames(bodyText: string): string[] {
|
||||
const lines = bodyText.split('\n').map(l => l.trim()).filter(l => l.length > 0);
|
||||
const formNames: string[] = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (lines[i] === 'Add' && i > 0 && lines[i - 1] !== 'Add' && lines[i - 1].length > 3) {
|
||||
formNames.push(lines[i - 1]);
|
||||
}
|
||||
}
|
||||
return formNames;
|
||||
}
|
||||
|
||||
async function downloadFormsInView(
|
||||
page: import('playwright').Page,
|
||||
context: import('playwright').BrowserContext,
|
||||
@@ -228,19 +241,35 @@ async function downloadFormsInView(
|
||||
downloaded: string[],
|
||||
failed: string[]
|
||||
) {
|
||||
// Flow: click form name → preview opens → click Download button → save file
|
||||
// Flow: scroll to load all forms, then click form name → preview → Download button → save
|
||||
|
||||
// Extract form names from the page body text — the list renders as "Name\nAdd\nName\nAdd..."
|
||||
const bodyText = await page.locator('body').innerText().catch(() => '');
|
||||
const lines = bodyText.split('\n').map(l => l.trim()).filter(l => l.length > 3);
|
||||
const formNames: string[] = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (lines[i] === 'Add' && i > 0 && lines[i - 1] !== 'Add' && lines[i - 1].length > 3) {
|
||||
formNames.push(lines[i - 1]);
|
||||
// Scroll down repeatedly to trigger infinite scroll, collecting all form names
|
||||
const allNames = new Set<string>();
|
||||
let prevCount = 0;
|
||||
let stallRounds = 0;
|
||||
while (stallRounds < 5) {
|
||||
// Scroll both window and any inner scrollable container to handle virtualized lists
|
||||
await page.evaluate(() => {
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
const scrollable = document.querySelector('[class*="scroll"], [class*="list"], main, [role="main"], .overflow-auto, .overflow-y-auto');
|
||||
if (scrollable) scrollable.scrollTop = scrollable.scrollHeight;
|
||||
});
|
||||
await page.waitForTimeout(2000);
|
||||
const bodyText = await page.locator('body').innerText().catch(() => '');
|
||||
for (const n of extractFormNames(bodyText)) allNames.add(n);
|
||||
if (allNames.size === prevCount) {
|
||||
stallRounds++;
|
||||
} else {
|
||||
stallRounds = 0;
|
||||
prevCount = allNames.size;
|
||||
process.stdout.write(` Loaded ${allNames.size} forms so far...\n`);
|
||||
}
|
||||
}
|
||||
const names = [...new Set(formNames)];
|
||||
// Scroll back to top before clicking
|
||||
await page.evaluate(() => window.scrollTo(0, 0));
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const names = [...allNames];
|
||||
console.log(` Found ${names.length} forms to download`);
|
||||
if (names.length === 0) {
|
||||
await page.screenshot({ path: `scripts/debug-no-forms-${Date.now()}.png` });
|
||||
@@ -285,7 +314,9 @@ async function downloadFormsInView(
|
||||
page.waitForEvent('download', { timeout: 20_000 }),
|
||||
downloadBtn.click(),
|
||||
]);
|
||||
await download.saveAs(destPath);
|
||||
const tmpPath = path.join(tmpdir(), `skyslope-${Date.now()}.tmp`);
|
||||
await download.saveAs(tmpPath);
|
||||
await savePdf(tmpPath, destPath);
|
||||
process.stdout.write(` ✓ ${sanitized}.pdf\n`);
|
||||
downloaded.push(sanitized);
|
||||
} catch (err) {
|
||||
@@ -379,6 +410,21 @@ async function interceptPdfOnClick(
|
||||
});
|
||||
}
|
||||
|
||||
/** If the downloaded file is a ZIP, extract the first PDF inside; otherwise move as-is. */
|
||||
async function savePdf(tmpPath: string, destPath: string) {
|
||||
const buf = await fs.readFile(tmpPath);
|
||||
const isPk = buf[0] === 0x50 && buf[1] === 0x4b; // PK magic bytes = ZIP
|
||||
if (isPk) {
|
||||
const zip = new AdmZip(buf);
|
||||
const entry = zip.getEntries().find(e => e.entryName.toLowerCase().endsWith('.pdf'));
|
||||
if (!entry) throw new Error('ZIP contained no PDF entry');
|
||||
await fs.writeFile(destPath, entry.getData());
|
||||
} else {
|
||||
await fs.rename(tmpPath, destPath);
|
||||
}
|
||||
await fs.unlink(tmpPath).catch(() => {}); // clean up tmp if rename didn't move it
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error('Fatal:', err.message);
|
||||
process.exit(1);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import "dotenv/config";
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import postgres from "postgres";
|
||||
import { users, clients, documents } from "../src/lib/db/schema";
|
||||
import { users } from "../src/lib/db/schema";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { inArray } from "drizzle-orm";
|
||||
|
||||
const client = postgres(process.env.DATABASE_URL!);
|
||||
const db = drizzle({ client });
|
||||
@@ -22,35 +21,6 @@ async function seed() {
|
||||
|
||||
console.log(`Seeded agent account: ${email}`);
|
||||
|
||||
// Seed clients
|
||||
await db.insert(clients).values([
|
||||
{ name: "Sarah Johnson", email: "sarah.j@example.com" },
|
||||
{ name: "Mike Torres", email: "m.torres@example.com" },
|
||||
]).onConflictDoNothing();
|
||||
|
||||
console.log("Seeded clients: Sarah Johnson, Mike Torres");
|
||||
|
||||
// Query back seeded clients to get their IDs
|
||||
const [sarah, mike] = await db
|
||||
.select({ id: clients.id })
|
||||
.from(clients)
|
||||
.where(inArray(clients.email, ["sarah.j@example.com", "m.torres@example.com"]))
|
||||
.orderBy(clients.createdAt);
|
||||
|
||||
// Seed placeholder documents
|
||||
if (sarah && mike) {
|
||||
await db.insert(documents).values([
|
||||
{ name: "Purchase Agreement - 842 Maple Dr", clientId: sarah.id, status: "Signed", sentAt: new Date("2026-02-15") },
|
||||
{ name: "Seller Disclosure - 842 Maple Dr", clientId: sarah.id, status: "Signed", sentAt: new Date("2026-02-14") },
|
||||
{ name: "Buyer Rep Agreement", clientId: mike.id, status: "Sent", sentAt: new Date("2026-03-10") },
|
||||
{ name: "Purchase Agreement - 1205 Oak Ave", clientId: mike.id, status: "Draft", sentAt: null },
|
||||
]).onConflictDoNothing();
|
||||
|
||||
console.log("Seeded 4 placeholder documents");
|
||||
} else {
|
||||
console.log("Skipping documents seed — clients not found");
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
|
||||