diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 25fa374..3f9cead 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -19,7 +19,8 @@ "Bash(find:*)", "mcp__context7__resolve-library-id", "mcp__context7__get-library-docs", - "mcp__serena__create_text_file" + "mcp__serena__create_text_file", + "Bash(curl:*)" ] }, "enableAllProjectMcpServers": true, diff --git a/app/ui/BillEditForm.tsx b/app/ui/BillEditForm.tsx index 79909db..7aa5dcc 100644 --- a/app/ui/BillEditForm.tsx +++ b/app/ui/BillEditForm.tsx @@ -9,8 +9,8 @@ import Link from "next/link"; import { formatYearMonth } from "../lib/format"; import { DecodeResult, findDecodePdf417 } from "../lib/pdf/barcodeDecoderWasm"; import { useLocale, useTranslations } from "next-intl"; -import { Pdf417Barcode } from "./Pdf417Barcode"; import { InfoBox } from "./InfoBox"; +import { Pdf417BarcodeWasm } from "./Pdf417BarcodeWasm"; // 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 @@ -203,7 +203,7 @@ export const BillEditForm: FC = ({ location, bill }) => { hub3aText ?

{t.rich('barcode-disclaimer', { br: () =>
})}

: null diff --git a/app/ui/Pdf417BarcodeWasm.tsx b/app/ui/Pdf417BarcodeWasm.tsx new file mode 100644 index 0000000..5adbb5e --- /dev/null +++ b/app/ui/Pdf417BarcodeWasm.tsx @@ -0,0 +1,76 @@ +'use client'; + +import { useState, useEffect, FC } from 'react'; +import { writeBarcode, prepareZXingModule, type WriterOptions } from 'zxing-wasm/writer'; + +// Configure WASM file location for writer +prepareZXingModule({ + overrides: { + locateFile: (path, prefix) => { + if (path.endsWith('.wasm')) { + return window.location.origin + '/zxing_writer.wasm'; + } + return prefix + path; + } + } +}); + +export const Pdf417BarcodeWasm: FC<{ hub3aText: string, className?: string }> = ({ hub3aText, className }) => { + const [barcodeDataUrl, setBarcodeDataUrl] = useState(undefined); + const [error, setError] = useState(undefined); + + useEffect(() => { + const generateBarcode = async () => { + try { + setError(undefined); + setBarcodeDataUrl(undefined); + + const writerOptions: WriterOptions = { + format: 'PDF417', + ecLevel: "5", + scale: 2, + }; + + const result = await writeBarcode(hub3aText, writerOptions); + + // Convert PNG blob to data URL + const reader = new FileReader(); + reader.onloadend = () => { + setBarcodeDataUrl(reader.result as string); + }; + reader.readAsDataURL(result.image as Blob); + + } catch (err) { + console.error('Failed to generate PDF417 barcode:', err); + setError('Failed to generate barcode'); + } + }; + + if (hub3aText) { + generateBarcode(); + } + }, [hub3aText]); + + // Show error state + if (error) { + return ( +
+ {error} +
+ ); + } + + // Don't render until barcode is generated (prevents hydration mismatch) + if (!barcodeDataUrl) { + return ( +
+ +
+ ); + } + + return ( + // eslint-disable-next-line @next/next/no-img-element + PDF417 Barcode + ); +} diff --git a/app/ui/PrintPreview.tsx b/app/ui/PrintPreview.tsx index 3de1f83..214d6a8 100644 --- a/app/ui/PrintPreview.tsx +++ b/app/ui/PrintPreview.tsx @@ -1,7 +1,7 @@ 'use client'; import { PrintBarcodeData } from '../lib/actions/printActions'; -import { Pdf417Barcode } from './Pdf417Barcode'; +import { Pdf417BarcodeWasm } from './Pdf417BarcodeWasm'; export interface PrintPreviewProps { data: PrintBarcodeData[]; @@ -132,21 +132,8 @@ export const PrintPreview: React.FC = ({ data, year, month, t
{ - item.hub3aText ? - - : ( - // LEGACY SUPPORT ... untill all bills have been migrated - - item.barcodeImage ? - // eslint-disable-next-line @next/next/no-img-element - {`Barcode : null - ) + item.hub3aText ? : null } -
diff --git a/app/ui/ViewBillCard.tsx b/app/ui/ViewBillCard.tsx index 8a6e1ef..8df55d2 100644 --- a/app/ui/ViewBillCard.tsx +++ b/app/ui/ViewBillCard.tsx @@ -7,8 +7,8 @@ import Link from "next/link"; import { useRouter } from "next/navigation"; import { formatYearMonth } from "../lib/format"; import { useTranslations } from "next-intl"; -import { Pdf417Barcode } from "./Pdf417Barcode"; import { uploadProofOfPayment } from "../lib/actions/billActions"; +import { Pdf417BarcodeWasm } from "./Pdf417BarcodeWasm"; export interface ViewBillCardProps { location: BillingLocation; @@ -111,7 +111,7 @@ export const ViewBillCard: FC = ({ location, bill, shareId }) hub3aText ?

{t.rich('barcode-disclaimer', { br: () =>
})}

: null diff --git a/app/ui/ViewLocationCard.tsx b/app/ui/ViewLocationCard.tsx index e0fd0b0..21a12fc 100644 --- a/app/ui/ViewLocationCard.tsx +++ b/app/ui/ViewLocationCard.tsx @@ -7,13 +7,13 @@ import { formatCurrency, formatIban } from "../lib/formatStrings"; import { useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; import { ViewBillBadge } from "./ViewBillBadge"; -import { Pdf417Barcode } from "./Pdf417Barcode"; import { EncodePayment, PaymentParams } from "hub-3a-payment-encoder"; import Link from "next/link"; import { LinkIcon } from "@heroicons/react/24/outline"; import { uploadUtilBillsProofOfPayment } from "../lib/actions/locationActions"; import QRCode from "react-qr-code"; import { TicketIcon } from "@heroicons/react/24/solid"; +import { Pdf417BarcodeWasm } from "./Pdf417BarcodeWasm"; export interface ViewLocationCardProps { location: BillingLocation; @@ -153,7 +153,7 @@ export const ViewLocationCard: FC = ({ location, userSett
  • {t("payment-reference-label")}
    {paymentParams.PozivNaBroj}
  • : null diff --git a/public/zxing_writer.wasm b/public/zxing_writer.wasm new file mode 100644 index 0000000..523e694 Binary files /dev/null and b/public/zxing_writer.wasm differ