Refactor: Complete barcodeImage to hub3aText migration across codebase
Database & Types: - Added hub3aText field to Bill interface in db-types.ts - Marked barcodeImage as @deprecated legacy field Server Actions: - Updated billActions to read/write hub3aText instead of barcodeImage - Commented out legacy barcodeImage code with migration notes Barcode Decoder: - Renamed image2canvas to file2canvas for clarity - Added new image2canvas function for base64 encoded images (migration support) - Added hub3aText to DecodeResult type - Exported decodeFromImage function for legacy data migration - Updated decoding logic to extract and return hub3aText UI Components: - Refactored Pdf417Barcode to accept hub3aText string instead of PaymentParams - Removed EncodePayment call from Pdf417Barcode (now expects pre-encoded text) - Updated ViewLocationCard to encode payment params before passing to Pdf417Barcode This completes the refactoring from storing bitmap images to storing decoded HUB-3A payment strings, providing more efficient storage and easier data manipulation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -146,7 +146,8 @@ export const updateOrAddBill = withUser(async (user:AuthenticatedUser, locationI
|
|||||||
|
|
||||||
const billPaid = formData.get('billPaid') === 'on';
|
const billPaid = formData.get('billPaid') === 'on';
|
||||||
const billedTo = (formData.get('billedTo') as BilledTo) ?? BilledTo.Tenant;
|
const billedTo = (formData.get('billedTo') as BilledTo) ?? BilledTo.Tenant;
|
||||||
const barcodeImage = formData.get('barcodeImage')?.valueOf() as string;
|
// const barcodeImage = formData.get('barcodeImage')?.valueOf() as string; // LEGACY FIELD - not used anymore
|
||||||
|
const hub3aText = formData.get('hub3aText')?.valueOf() as string;
|
||||||
|
|
||||||
// update the bill in the mongodb
|
// update the bill in the mongodb
|
||||||
const dbClient = await getDbClient();
|
const dbClient = await getDbClient();
|
||||||
@@ -164,7 +165,8 @@ export const updateOrAddBill = withUser(async (user:AuthenticatedUser, locationI
|
|||||||
"bills.$[elem].attachment": billAttachment,
|
"bills.$[elem].attachment": billAttachment,
|
||||||
"bills.$[elem].notes": billNotes,
|
"bills.$[elem].notes": billNotes,
|
||||||
"bills.$[elem].payedAmount": payedAmount,
|
"bills.$[elem].payedAmount": payedAmount,
|
||||||
"bills.$[elem].barcodeImage": barcodeImage,
|
// "bills.$[elem].barcodeImage": barcodeImage, // LEGACY FIELD - not used anymore
|
||||||
|
"bills.$[elem].hub3aText": hub3aText,
|
||||||
|
|
||||||
}: {
|
}: {
|
||||||
"bills.$[elem].name": billName,
|
"bills.$[elem].name": billName,
|
||||||
@@ -172,7 +174,8 @@ export const updateOrAddBill = withUser(async (user:AuthenticatedUser, locationI
|
|||||||
"bills.$[elem].billedTo": billedTo,
|
"bills.$[elem].billedTo": billedTo,
|
||||||
"bills.$[elem].notes": billNotes,
|
"bills.$[elem].notes": billNotes,
|
||||||
"bills.$[elem].payedAmount": payedAmount,
|
"bills.$[elem].payedAmount": payedAmount,
|
||||||
"bills.$[elem].barcodeImage": barcodeImage,
|
// "bills.$[elem].barcodeImage": barcodeImage, // LEGACY FIELD - not used anymore
|
||||||
|
"bills.$[elem].hub3aText": hub3aText,
|
||||||
};
|
};
|
||||||
|
|
||||||
// find a location with the given locationID
|
// find a location with the given locationID
|
||||||
@@ -198,7 +201,8 @@ export const updateOrAddBill = withUser(async (user:AuthenticatedUser, locationI
|
|||||||
attachment: billAttachment,
|
attachment: billAttachment,
|
||||||
notes: billNotes,
|
notes: billNotes,
|
||||||
payedAmount,
|
payedAmount,
|
||||||
barcodeImage,
|
// barcodeImage, // LEGACY FIELD - not used anymore
|
||||||
|
hub3aText,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add to current location
|
// Add to current location
|
||||||
@@ -262,7 +266,8 @@ export const updateOrAddBill = withUser(async (user:AuthenticatedUser, locationI
|
|||||||
attachment: null, // No attachment for subsequent months
|
attachment: null, // No attachment for subsequent months
|
||||||
notes: billNotes,
|
notes: billNotes,
|
||||||
payedAmount: null,
|
payedAmount: null,
|
||||||
barcodeImage: undefined,
|
// barcodeImage: undefined, // LEGACY FIELD - not used anymore
|
||||||
|
hub3aText: undefined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,11 @@ export interface Bill {
|
|||||||
hasAttachment?: boolean;
|
hasAttachment?: boolean;
|
||||||
/** (optional) notes */
|
/** (optional) notes */
|
||||||
notes?: string|null;
|
notes?: string|null;
|
||||||
/** (optional) image data containing PDF471 bar code */
|
/**
|
||||||
|
* (optional) image data containing PDF471 bar code
|
||||||
|
* @deprecated LEGACY FIELD - use hub3aText instead
|
||||||
|
* */
|
||||||
barcodeImage?:string;
|
barcodeImage?:string;
|
||||||
|
/** (optional) HUB-3A text for generating PDF417 bar code */
|
||||||
|
hub3aText?:string;
|
||||||
};
|
};
|
||||||
@@ -93,7 +93,7 @@ const parseHubText = (text: string):BillInfo => {
|
|||||||
* @param {File} imageFile - a file containing an image
|
* @param {File} imageFile - a file containing an image
|
||||||
* @return {Promise<HTMLCanvasElement>} the canvas with the image rendered onto it
|
* @return {Promise<HTMLCanvasElement>} the canvas with the image rendered onto it
|
||||||
*/
|
*/
|
||||||
const image2canvas = async function (imageFile:File): Promise<HTMLCanvasElement> {
|
const file2canvas = async function (imageFile:File): Promise<HTMLCanvasElement> {
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
@@ -127,9 +127,40 @@ const image2canvas = async function (imageFile:File): Promise<HTMLCanvasElement>
|
|||||||
});
|
});
|
||||||
|
|
||||||
return(canvas);
|
return(canvas);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render an image from onto a canvas.
|
||||||
|
* @param {String} image - base64 encoded image string
|
||||||
|
* @return {Promise<HTMLCanvasElement>} the canvas with the image rendered onto it
|
||||||
|
*/
|
||||||
|
const image2canvas = async function (imageBase64:string): Promise<HTMLCanvasElement> {
|
||||||
|
|
||||||
|
const canvas = await new Promise<HTMLCanvasElement>((resolve, reject) => {
|
||||||
|
const img = new Image();
|
||||||
|
|
||||||
|
img.onload = () => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
|
||||||
|
if(!ctx) {
|
||||||
|
reject("Context is not set")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
|
||||||
|
resolve(canvas);
|
||||||
|
};
|
||||||
|
|
||||||
|
img.src = imageBase64;
|
||||||
|
});
|
||||||
|
|
||||||
|
return(canvas);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the first page of a PDF document onto a new canvas.
|
* Render the first page of a PDF document onto a new canvas.
|
||||||
* @param {File} pdfFile - a file containing a PDF document
|
* @param {File} pdfFile - a file containing a PDF document
|
||||||
@@ -173,6 +204,7 @@ const pdf2canvas = async function (pdfFile:File): Promise<Array<HTMLCanvasElemen
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type DecodeResult = {
|
export type DecodeResult = {
|
||||||
|
hub3aText: string,
|
||||||
billInfo: BillInfo,
|
billInfo: BillInfo,
|
||||||
barcodeImage: string,
|
barcodeImage: string,
|
||||||
};
|
};
|
||||||
@@ -240,10 +272,12 @@ const decodeFromCanvas = async (canvas:HTMLCanvasElement): Promise<Array<DecodeR
|
|||||||
|
|
||||||
|
|
||||||
const result = await codeReader.decodeFromCanvas(sectionCanvas);
|
const result = await codeReader.decodeFromCanvas(sectionCanvas);
|
||||||
|
const hub3aText = result.getText()
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
codesFoundInSection.push({
|
codesFoundInSection.push({
|
||||||
billInfo: parseHubText(result.getText()),
|
hub3aText,
|
||||||
|
billInfo: parseHubText(hub3aText),
|
||||||
barcodeImage: copyBarcodeImage(sectionCanvas, result)
|
barcodeImage: copyBarcodeImage(sectionCanvas, result)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -329,12 +363,17 @@ const copyBarcodeImage = (canvas:HTMLCanvasElement, decoderResult:Result):string
|
|||||||
return(dataURL);
|
return(dataURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Finds PDF417 code within a base64 encoded image and decodes it */
|
||||||
|
export const decodeFromImage = async (imageBase64:string): Promise<DecodeResult[]|null> => {
|
||||||
|
return(await decodeFromCanvas( await image2canvas(imageBase64) ));
|
||||||
|
}
|
||||||
|
|
||||||
/** Finds PDF417 code within a file and decodes it */
|
/** Finds PDF417 code within a file and decodes it */
|
||||||
const decodeFromFile = async (file:File): Promise<DecodeResult[]|null> => {
|
const decodeFromFile = async (file:File): Promise<DecodeResult[]|null> => {
|
||||||
switch(file.type) {
|
switch(file.type) {
|
||||||
case 'image/png':
|
case 'image/png':
|
||||||
case 'image/jpeg':
|
case 'image/jpeg':
|
||||||
return(await decodeFromCanvas( await image2canvas(file) ));
|
return(await decodeFromCanvas( await file2canvas(file) ));
|
||||||
case 'application/pdf':
|
case 'application/pdf':
|
||||||
const pageCanvas = await pdf2canvas(file);
|
const pageCanvas = await pdf2canvas(file);
|
||||||
// go through each page of the PDF and decode the PDF417 codes
|
// go through each page of the PDF and decode the PDF417 codes
|
||||||
|
|||||||
@@ -5,16 +5,14 @@ import { generateBarcode } from '../lib/pdf/pdf417';
|
|||||||
import { renderBarcode } from '../lib/pdf/renderBarcode';
|
import { renderBarcode } from '../lib/pdf/renderBarcode';
|
||||||
import { EncodePayment, PaymentParams } from 'hub-3a-payment-encoder';
|
import { EncodePayment, PaymentParams } from 'hub-3a-payment-encoder';
|
||||||
|
|
||||||
export const Pdf417Barcode:FC<{paymentParams:PaymentParams}> = ({paymentParams}) => {
|
export const Pdf417Barcode:FC<{hub3aText:string}> = ({hub3aText: hub3a_text}) => {
|
||||||
const [bitmapData, setBitmapData] = useState<string | undefined>(undefined);
|
const [bitmapData, setBitmapData] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const hub3a_text = EncodePayment(paymentParams);
|
|
||||||
|
|
||||||
const barcodeMatrix = generateBarcode(hub3a_text);
|
const barcodeMatrix = generateBarcode(hub3a_text);
|
||||||
const bitmap = renderBarcode(barcodeMatrix, 2, 2);
|
const bitmap = renderBarcode(barcodeMatrix, 2, 2);
|
||||||
setBitmapData(bitmap);
|
setBitmapData(bitmap);
|
||||||
}, [paymentParams]);
|
}, [hub3a_text]);
|
||||||
|
|
||||||
// Don't render until bitmap is generated (prevents hydration mismatch)
|
// Don't render until bitmap is generated (prevents hydration mismatch)
|
||||||
if (!bitmapData) {
|
if (!bitmapData) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { formatCurrency } from "../lib/formatStrings";
|
|||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { ViewBillBadge } from "./ViewBillBadge";
|
import { ViewBillBadge } from "./ViewBillBadge";
|
||||||
import { Pdf417Barcode } from "./Pdf417Barcode";
|
import { Pdf417Barcode } from "./Pdf417Barcode";
|
||||||
import { PaymentParams } from "hub-3a-payment-encoder";
|
import { EncodePayment, PaymentParams } from "hub-3a-payment-encoder";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { DocumentIcon } from "@heroicons/react/24/outline";
|
import { DocumentIcon } from "@heroicons/react/24/outline";
|
||||||
import { uploadUtilBillsProofOfPayment } from "../lib/actions/locationActions";
|
import { uploadUtilBillsProofOfPayment } from "../lib/actions/locationActions";
|
||||||
@@ -87,6 +87,8 @@ export const ViewLocationCard:FC<ViewLocationCardProps> = ({location, userSettin
|
|||||||
OpisPlacanja: `Režije-${locationNameTrimmed_max20}-${formatYearMonth(yearMonth)}`, // max length 35 = "Režije-" (7) + locationName (20) + "-" (1) + "YYYY-MM" (7)
|
OpisPlacanja: `Režije-${locationNameTrimmed_max20}-${formatYearMonth(yearMonth)}`, // max length 35 = "Režije-" (7) + locationName (20) + "-" (1) + "YYYY-MM" (7)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hub3a_text = EncodePayment(paymentParams);
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<div data-key={_id } className="card card-compact card-bordered max-w-[30em] min-w-[350px] bg-base-100 border-1 border-neutral my-1">
|
<div data-key={_id } className="card card-compact card-bordered max-w-[30em] min-w-[350px] bg-base-100 border-1 border-neutral my-1">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
@@ -118,7 +120,7 @@ export const ViewLocationCard:FC<ViewLocationCardProps> = ({location, userSettin
|
|||||||
<li><strong>{t("payment-reference-label")}</strong><pre className="inline pl-1">{paymentParams.PozivNaBroj}</pre></li>
|
<li><strong>{t("payment-reference-label")}</strong><pre className="inline pl-1">{paymentParams.PozivNaBroj}</pre></li>
|
||||||
<li><strong>{t("payment-purpose-code-label")}</strong><pre className="inline pl-1">{paymentParams.SifraNamjene}</pre></li>
|
<li><strong>{t("payment-purpose-code-label")}</strong><pre className="inline pl-1">{paymentParams.SifraNamjene}</pre></li>
|
||||||
</ul>
|
</ul>
|
||||||
<Pdf417Barcode paymentParams={paymentParams} />
|
<Pdf417Barcode hub3aText={hub3a_text} />
|
||||||
</>
|
</>
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user