typescript fixin
Some checks failed
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Failing after 14s

This commit is contained in:
2025-11-12 10:01:05 -08:00
parent ae6d934e2c
commit a54b96810a
6 changed files with 267 additions and 39 deletions

18
App.tsx
View File

@@ -4,6 +4,7 @@ import { FlyerDisplay } from './components/FlyerDisplay';
import { ExtractedDataTable } from './components/ExtractedDataTable';
import { AnalysisPanel } from './components/AnalysisPanel';
import { PriceChart } from './components/PriceChart';
import * as pdfjsLib from 'pdfjs-dist';
import { ErrorDisplay } from './components/ErrorDisplay';
import { Header } from './components/Header';
import { logger } from './services/logger';
@@ -35,9 +36,8 @@ type AuthStatus = 'SIGNED_OUT' | 'ANONYMOUS' | 'AUTHENTICATED';
// pdf.js worker configuration
// This is crucial for allowing pdf.js to process PDFs in a separate thread, preventing the UI from freezing.
// We need to explicitly tell pdf.js where to load its worker script from.
// Since we're using a CDN for the main library, we'll use the same CDN for the worker.
// @ts-ignore - pdfjsLib is globally available from the script tag in index.html
pdfjsLib.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.mjs`;
// By importing pdfjs-dist, we can host the worker locally, which is more reliable.
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.mjs', import.meta.url).toString();
function App() {
const [flyers, setFlyers] = useState<Flyer[]>([]);
@@ -107,6 +107,18 @@ function App() {
}
}, [profile]);
// Effect to mark the app as "ready" for data fetching.
// This replaces the onReady callback from the now-removed SystemCheck component.
useEffect(() => {
if (!isDbConnected) {
setIsReady(true);
} else {
// A short delay to allow other components to initialize.
const timer = setTimeout(() => setIsReady(true), 250);
return () => clearTimeout(timer);
}
}, [isDbConnected]);
// This is the login handler that will be passed to the LoginPage component.
const handleLogin = async (email: string, pass: string) => {
if (!supabase) return;

View File

@@ -3,8 +3,10 @@ import { FlyerDisplay } from './FlyerDisplay';
import { ExtractedDataTable } from './ExtractedDataTable';
import { AnalysisPanel } from './AnalysisPanel';
import { PriceChart } from './PriceChart';
import * as pdfjsLib from 'pdfjs-dist';
import { ErrorDisplay } from './ErrorDisplay';
import { Header } from './Header';
import { logger } from '../services/logger';
import { isImageAFlyer, extractCoreDataFromImage, extractAddressFromImage, extractLogoFromImage } from '../services/geminiService';
import type { FlyerItem, Flyer, MasterGroceryItem, DealItem, ProcessingStage, StageStatus, Store, Profile, ShoppingList, ShoppingListItem } from '../types';
import type { Database } from '../types/supabase'; // Correctly import the Database type
@@ -130,8 +132,9 @@ function App() {
try {
const allFlyers = await getFlyers();
setFlyers(allFlyers);
} catch(e: any) {
setError(e.message);
} catch(e) {
const errorMessage = e instanceof Error ? e.message : String(e);
setError(errorMessage);
}
}, []);
@@ -143,8 +146,9 @@ function App() {
try {
const items = await getWatchedItems(userId);
setWatchedItems(items);
} catch (e: any) {
setError(`Could not fetch watched items: ${e.message}`);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
setError(`Could not fetch watched items: ${errorMessage}`);
}
}, []);
@@ -162,8 +166,9 @@ function App() {
} else if (lists.length === 0) {
setActiveListId(null);
}
} catch (e: any) {
setError(`Could not fetch shopping lists: ${e.message}`);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
setError(`Could not fetch shopping lists: ${errorMessage}`);
}
}, [activeListId]);
@@ -172,8 +177,9 @@ function App() {
try {
const items = await getAllMasterItems();
setMasterItems(items);
} catch (e: any) {
setError(`Could not fetch master item list: ${e.message}`);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
setError(`Could not fetch master item list: ${errorMessage}`);
}
}, []);
@@ -243,8 +249,9 @@ function App() {
try {
const items = await getFlyerItems(flyer.id);
setFlyerItems(items);
} catch (e: any) {
setError(e.message);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
setError(errorMessage);
}
}, []);
@@ -305,8 +312,9 @@ function App() {
}));
setActiveDeals(deals);
} catch (e: any) {
setError(`Could not fetch active deals: ${e.message}`);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
setError(`Could not fetch active deals: ${errorMessage}`);
} finally {
setActiveDealsLoading(false);
}
@@ -346,8 +354,9 @@ function App() {
const validFlyerIds = validFlyers.map(f => f.id);
const totalCount = await countFlyerItemsForFlyers(validFlyerIds);
setTotalActiveItems(totalCount);
} catch (e: any) {
console.error("Failed to calculate total active items:", e.message);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
logger.error("Failed to calculate total active items:", { error: errorMessage });
setTotalActiveItems(0);
}
};
@@ -416,8 +425,9 @@ function App() {
updateStage?.(stageIndex, { status: 'in-progress' });
storeAddress = await withTimeout(extractAddressFromImage(files[0]), nonCriticalTimeout);
updateStage?.(stageIndex++, { status: 'completed' }); // stageIndex is now 4
} catch (e: any) {
console.warn("Non-critical step failed: Address extraction.", e.message);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
logger.warn("Non-critical step failed: Address extraction.", { error: errorMessage });
updateStage?.(stageIndex++, { status: 'error', detail: '(Skipped)' }); // stageIndex is now 4
}
@@ -428,8 +438,9 @@ function App() {
const logoData = await withTimeout(extractLogoFromImage(files.slice(0, 1)), nonCriticalTimeout);
storeLogoBase64 = logoData.store_logo_base_64;
updateStage?.(stageIndex++, { status: 'completed' }); // stageIndex is now 5
} catch (e: any) {
console.warn("Non-critical step failed: Logo extraction.", e.message);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
logger.warn("Non-critical step failed: Logo extraction.", { error: errorMessage });
updateStage?.(stageIndex++, { status: 'error', detail: '(Skipped)' }); // stageIndex is now 5
}
@@ -607,8 +618,9 @@ function App() {
}
return prevItems; // Item already existed in list
});
} catch (e: any) {
setError(`Could not add watched item: ${e.message}`);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
setError(`Could not add watched item: ${errorMessage}`);
await fetchWatchedItems(session?.user?.id);
}
}, [session, fetchWatchedItems]);
@@ -618,8 +630,9 @@ function App() {
try {
await removeWatchedItem(session.user.id, masterItemId);
setWatchedItems(prevItems => prevItems.filter(item => item.id !== masterItemId));
} catch (e: any) {
setError(`Could not remove watched item: ${e.message}`);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
setError(`Could not remove watched item: ${errorMessage}`);
}
}, [session]);
@@ -630,8 +643,9 @@ function App() {
const newList = await createShoppingList(session.user.id, name);
setShoppingLists(prev => [...prev, newList]);
setActiveListId(newList.id);
} catch (e: any) {
setError(`Could not create list: ${e.message}`);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
setError(`Could not create list: ${errorMessage}`);
}
}, [session]);
@@ -644,8 +658,9 @@ function App() {
if (activeListId === listId) {
setActiveListId(newLists.length > 0 ? newLists[0].id : null);
}
} catch (e: any) {
setError(`Could not delete list: ${e.message}`);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
setError(`Could not delete list: ${errorMessage}`);
}
}, [session, shoppingLists, activeListId]);
@@ -662,8 +677,9 @@ function App() {
}
return list;
}));
} catch (e: any) {
setError(`Could not add item to list: ${e.message}`);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
setError(`Could not add item to list: ${errorMessage}`);
}
}, [session]);
@@ -677,8 +693,9 @@ function App() {
}
return list;
}));
} catch (e: any) {
setError(`Could not update list item: ${e.message}`);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
setError(`Could not update list item: ${errorMessage}`);
}
}, [session, activeListId]);
@@ -692,8 +709,9 @@ function App() {
}
return list;
}));
} catch (e: any) {
setError(`Could not remove list item: ${e.message}`);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e);
setError(`Could not remove list item: ${errorMessage}`);
}
}, [session, activeListId]);

View File

@@ -62,7 +62,7 @@ export const SystemCheck: React.FC<SystemCheckProps> = ({ onReady }) => {
for (const key in results) {
// Ensure the property belongs to the object itself.
if (Object.prototype.hasOwnProperty.call(results, key)) {
const { pass, message } = (results as any)[key];
const { pass, message } = (results as Record<string, { pass: boolean; message: string; }>)[key];
updateCheckStatus(key, pass ? 'pass' : 'fail', message);
if (!pass) {
allTestsPassed = false;

198
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"dependencies": {
"@google/genai": "^1.29.0",
"@supabase/supabase-js": "^2.81.1",
"pdfjs-dist": "^5.4.394",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.9.5",
@@ -1341,6 +1342,191 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@napi-rs/canvas": {
"version": "0.1.82",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.82.tgz",
"integrity": "sha512-FGjyUBoF0sl1EenSiE4UV2WYu76q6F9GSYedq5EiOCOyGYoQ/Owulcv6rd7v/tWOpljDDtefXXIaOCJrVKem4w==",
"license": "MIT",
"optional": true,
"workspaces": [
"e2e/*"
],
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@napi-rs/canvas-android-arm64": "0.1.82",
"@napi-rs/canvas-darwin-arm64": "0.1.82",
"@napi-rs/canvas-darwin-x64": "0.1.82",
"@napi-rs/canvas-linux-arm-gnueabihf": "0.1.82",
"@napi-rs/canvas-linux-arm64-gnu": "0.1.82",
"@napi-rs/canvas-linux-arm64-musl": "0.1.82",
"@napi-rs/canvas-linux-riscv64-gnu": "0.1.82",
"@napi-rs/canvas-linux-x64-gnu": "0.1.82",
"@napi-rs/canvas-linux-x64-musl": "0.1.82",
"@napi-rs/canvas-win32-x64-msvc": "0.1.82"
}
},
"node_modules/@napi-rs/canvas-android-arm64": {
"version": "0.1.82",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.82.tgz",
"integrity": "sha512-bvZhN0iI54ouaQOrgJV96H2q7J3ZoufnHf4E1fUaERwW29Rz4rgicohnAg4venwBJZYjGl5Yl3CGmlAl1LZowQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-darwin-arm64": {
"version": "0.1.82",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.82.tgz",
"integrity": "sha512-InuBHKCyuFqhNwNr4gpqazo5Xp6ltKflqOLiROn4hqAS8u21xAHyYCJRgHwd+a5NKmutFTaRWeUIT/vxWbU/iw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-darwin-x64": {
"version": "0.1.82",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.82.tgz",
"integrity": "sha512-aQGV5Ynn96onSXcuvYb2y7TRXD/t4CL2EGmnGqvLyeJX1JLSNisKQlWN/1bPDDXymZYSdUqbXehj5qzBlOx+RQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
"version": "0.1.82",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.82.tgz",
"integrity": "sha512-YIUpmHWeHGGRhWitT1KJkgj/JPXPfc9ox8oUoyaGPxolLGPp5AxJkq8wIg8CdFGtutget968dtwmx71m8o3h5g==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-arm64-gnu": {
"version": "0.1.82",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.82.tgz",
"integrity": "sha512-AwLzwLBgmvk7kWeUgItOUor/QyG31xqtD26w1tLpf4yE0hiXTGp23yc669aawjB6FzgIkjh1NKaNS52B7/qEBQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-arm64-musl": {
"version": "0.1.82",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.82.tgz",
"integrity": "sha512-moZWuqepAwWBffdF4JDadt8TgBD02iMhG6I1FHZf8xO20AsIp9rB+p0B8Zma2h2vAF/YMjeFCDmW5un6+zZz9g==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
"version": "0.1.82",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.82.tgz",
"integrity": "sha512-w9++2df2kG9eC9LWYIHIlMLuhIrKGQYfUxs97CwgxYjITeFakIRazI9LYWgVzEc98QZ9x9GQvlicFsrROV59MQ==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-x64-gnu": {
"version": "0.1.82",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.82.tgz",
"integrity": "sha512-lZulOPwrRi6hEg/17CaqdwWEUfOlIJuhXxincx1aVzsVOCmyHf+xFq4i6liJl1P+x2v6Iz2Z/H5zHvXJCC7Bwg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-x64-musl": {
"version": "0.1.82",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.82.tgz",
"integrity": "sha512-Be9Wf5RTv1w6GXlTph55K3PH3vsAh1Ax4T1FQY1UYM0QfD0yrwGdnJ8/fhqw7dEgMjd59zIbjJQC8C3msbGn5g==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-win32-x64-msvc": {
"version": "0.1.82",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.82.tgz",
"integrity": "sha512-LN/i8VrvxTDmEEK1c10z2cdOTkWT76LlTGtyZe5Kr1sqoSomKeExAjbilnu1+oee5lZUgS5yfZ2LNlVhCeARuw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -6157,6 +6343,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/pdfjs-dist": {
"version": "5.4.394",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.394.tgz",
"integrity": "sha512-9ariAYGqUJzx+V/1W4jHyiyCep6IZALmDzoaTLZ6VNu8q9LWi1/ukhzHgE2Xsx96AZi0mbZuK4/ttIbqSbLypg==",
"license": "Apache-2.0",
"engines": {
"node": ">=20.16.0 || >=22.3.0"
},
"optionalDependencies": {
"@napi-rs/canvas": "^0.1.81"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",

View File

@@ -13,6 +13,7 @@
"dependencies": {
"@google/genai": "^1.29.0",
"@supabase/supabase-js": "^2.81.1",
"pdfjs-dist": "^5.4.394",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.9.5",
@@ -20,16 +21,16 @@
"supabase": "^2.58.5"
},
"devDependencies": {
"@testing-library/react": "^16.3.0",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
"@types/node": "^24.10.1",
"@typescript-eslint/eslint-plugin": "^8.46.4",
"@typescript-eslint/parser": "^8.46.4",
"@vitejs/plugin-react": "^5.1.0",
"@vitest/coverage-v8": "^4.0.8",
"autoprefixer": "^10.4.22",
"eslint-plugin-react": "^7.35.0",
"eslint": "^9.39.1",
"eslint-plugin-react": "^7.35.0",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^15.9.0",

View File

@@ -1,5 +1,4 @@
// vitest.setup.ts
import { expect } from 'vitest';
import '@testing-library/jest-dom/vitest';
// You can add any other global setup here if needed.