From d7626827df643f7ce24e9f67dff7ae1d2d98f650 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Nov 2025 22:03:52 +0000 Subject: [PATCH 1/3] Fix QR code verification page params handling for Next.js 15 In Next.js 15, dynamic route params are passed as a Promise that must be unwrapped using React.use(). The verification page was accessing params.hashId directly, causing it to be undefined and resulting in API calls to /api/verify/undefined which failed with 500 errors. --- pascals-ledger/src/app/verify/[hashId]/page.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pascals-ledger/src/app/verify/[hashId]/page.tsx b/pascals-ledger/src/app/verify/[hashId]/page.tsx index 9050929..4f4cb57 100644 --- a/pascals-ledger/src/app/verify/[hashId]/page.tsx +++ b/pascals-ledger/src/app/verify/[hashId]/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useEffect } from 'react'; +import { use, useState, useEffect } from 'react'; import Link from 'next/link'; import Image from 'next/image'; @@ -16,18 +16,19 @@ interface VerificationData { verified: boolean; } -export default function VerifyPage({ params }: { params: { hashId: string } }) { +export default function VerifyPage({ params }: { params: Promise<{ hashId: string }> }) { + const { hashId } = use(params); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); useEffect(() => { fetchVerificationData(); - }, [params.hashId]); + }, [hashId]); const fetchVerificationData = async () => { try { - const response = await fetch(`/api/verify/${params.hashId}`); + const response = await fetch(`/api/verify/${hashId}`); const result = await response.json(); if (result.success) { From b517fd097cbcfea85e2c2eec058e47211a0826d4 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Nov 2025 22:59:41 +0000 Subject: [PATCH 2/3] Add IP-based location fallback and improve entropy display - Backend now falls back to IP geolocation when browser location is unavailable - Verification page shows "No environmental data collected" when entropy is empty - Fixed location display to handle both city-only and city+country formats --- pascals-ledger/src/app/verify/[hashId]/page.tsx | 15 ++++++++++----- pascals-ledger/src/lib/entropy.ts | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/pascals-ledger/src/app/verify/[hashId]/page.tsx b/pascals-ledger/src/app/verify/[hashId]/page.tsx index 4f4cb57..76787e8 100644 --- a/pascals-ledger/src/app/verify/[hashId]/page.tsx +++ b/pascals-ledger/src/app/verify/[hashId]/page.tsx @@ -156,16 +156,21 @@ export default function VerifyPage({ params }: { params: Promise<{ hashId: strin Environmental Entropy
- {data?.entropyMetadata?.weather && ( + {data?.entropyMetadata?.weather ? (

Weather: {data.entropyMetadata.weather.temperature}°C,{' '} {data.entropyMetadata.weather.conditions}

- )} - {data?.entropyMetadata?.location && ( + ) : null} + {data?.entropyMetadata?.location ? (

- Location: {data.entropyMetadata.location.city},{' '} - {data.entropyMetadata.location.country} + Location: {data.entropyMetadata.location.city} + {data.entropyMetadata.location.country && `, ${data.entropyMetadata.location.country}`} +

+ ) : null} + {!data?.entropyMetadata?.weather && !data?.entropyMetadata?.location && ( +

+ No environmental data collected for this hash

)}
diff --git a/pascals-ledger/src/lib/entropy.ts b/pascals-ledger/src/lib/entropy.ts index ea2fa35..c3a3799 100644 --- a/pascals-ledger/src/lib/entropy.ts +++ b/pascals-ledger/src/lib/entropy.ts @@ -106,6 +106,20 @@ export async function generateEntropyMetadata( location = { city: options.city, }; + } else { + // Fallback to IP-based geolocation + const ipLocation = await getIPLocation(); + if (ipLocation) { + weather = await fetchWeatherData(ipLocation.lat, ipLocation.lon); + location = { + city: ipLocation.city, + country: ipLocation.country, + coordinates: { + lat: ipLocation.lat, + lon: ipLocation.lon, + }, + }; + } } const metadata: EntropyMetadata = { From 9c338e32ea399ff7b3ed4047d9ad768ae82adf42 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 19 Nov 2025 11:12:58 +0000 Subject: [PATCH 3/3] Display truncated coordinates on verification page Shows approximate lat/lon (integer only) for transparency while protecting user privacy. Clarifies that precise coordinates are used in the actual hash. --- .../src/app/verify/[hashId]/page.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/pascals-ledger/src/app/verify/[hashId]/page.tsx b/pascals-ledger/src/app/verify/[hashId]/page.tsx index 76787e8..a8ff063 100644 --- a/pascals-ledger/src/app/verify/[hashId]/page.tsx +++ b/pascals-ledger/src/app/verify/[hashId]/page.tsx @@ -155,7 +155,7 @@ export default function VerifyPage({ params }: { params: Promise<{ hashId: strin

Environmental Entropy

-
+
{data?.entropyMetadata?.weather ? (

Weather: {data.entropyMetadata.weather.temperature}°C,{' '} @@ -163,10 +163,18 @@ export default function VerifyPage({ params }: { params: Promise<{ hashId: strin

) : null} {data?.entropyMetadata?.location ? ( -

- Location: {data.entropyMetadata.location.city} - {data.entropyMetadata.location.country && `, ${data.entropyMetadata.location.country}`} -

+ <> +

+ Location: {data.entropyMetadata.location.city} + {data.entropyMetadata.location.country && `, ${data.entropyMetadata.location.country}`} +

+ {data.entropyMetadata.location.coordinates && ( +

+ Approx. coordinates: {Math.floor(data.entropyMetadata.location.coordinates.lat)}°, {Math.floor(data.entropyMetadata.location.coordinates.lon)}° + (truncated for privacy - precise coordinates used in hash) +

+ )} + ) : null} {!data?.entropyMetadata?.weather && !data?.entropyMetadata?.location && (