From e3185238872114ec9cc4c933911235f75838daac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Fri, 9 Jan 2026 18:16:53 +0100 Subject: [PATCH] (bugfix) Fix proof of payment download URL to use shareID with checksum Fixed bug where proof of payment download links used raw locationID instead of shareID (locationID + checksum), causing link validation to fail. Added AsyncLink component to handle async shareID generation gracefully. Changes: - BillEditForm: Generate shareID using generateShareId server action - BillEditForm: Use AsyncLink to prevent broken links during async load - AsyncLink: New reusable component for links that need async data - Updated download URL from locationID-billID to shareID-billID format Co-Authored-By: Claude Sonnet 4.5 --- web-app/app/ui/AsyncLink.tsx | 13 +++++++++++++ web-app/app/ui/BillEditForm.tsx | 25 +++++++++++++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 web-app/app/ui/AsyncLink.tsx diff --git a/web-app/app/ui/AsyncLink.tsx b/web-app/app/ui/AsyncLink.tsx new file mode 100644 index 0000000..ece2643 --- /dev/null +++ b/web-app/app/ui/AsyncLink.tsx @@ -0,0 +1,13 @@ +import React from "react"; +import Link from "next/link"; + +/** Link component that can be disabled */ +export const AsyncLink: React.FC<{ href: string; children: React.ReactNode, target?: string, className?: string, disabled?: boolean }> = ({ href, children, target, className, disabled }) => + disabled ? {children} : + + {children} + diff --git a/web-app/app/ui/BillEditForm.tsx b/web-app/app/ui/BillEditForm.tsx index 846c400..edcb2f9 100644 --- a/web-app/app/ui/BillEditForm.tsx +++ b/web-app/app/ui/BillEditForm.tsx @@ -2,7 +2,7 @@ import { DocumentIcon, TicketIcon, TrashIcon } from "@heroicons/react/24/outline"; import { Bill, BilledTo, BillingLocation } from "../lib/db-types"; -import React, { FC, useEffect } from "react"; +import React, { FC, useEffect, useState } from "react"; import { useFormState } from "react-dom"; import { updateOrAddBill } from "../lib/actions/billActions"; import Link from "next/link"; @@ -11,6 +11,8 @@ import { DecodeResult, findDecodePdf417 } from "../lib/pdf/barcodeDecoderWasm"; import { useLocale, useTranslations } from "next-intl"; import { InfoBox } from "./InfoBox"; import { Pdf417Barcode } from "./Pdf417Barcode"; +import { generateShareId } from "../lib/actions/locationActions"; +import { AsyncLink } from "./AsyncLink"; // Next.js does not encode an utf-8 file name correctly when sending a form with a file attachment // This is a workaround for that @@ -35,6 +37,20 @@ export const BillEditForm: FC = ({ location, bill }) => { const { yearMonth: { year: billYear, month: billMonth }, _id: locationID, proofOfPaymentType } = location; + /** + * Share ID for viewing-only links (locationID + checksum) + * Note: This is different from the share button which calls `generateShareLink` + * to activate sharing and set TTL in the database + */ + const [shareID, setShareID] = useState(null); + + useEffect(() => { + // share ID can be generated server-side since it requires a secret key + // which we don't want to expose to the client + (async () => setShareID(await generateShareId(locationID)))(); + }, [locationID]); + + const initialState = { message: null, errors: {} }; const handleAction = updateOrAddBillMiddleware.bind(null, locationID, billID, billYear, billMonth); @@ -238,14 +254,15 @@ export const BillEditForm: FC = ({ location, bill }) => { // -> don't show anything proofOfPayment.fileName ? (
- {decodeURIComponent(proofOfPayment.fileName)} - +
) : null }