(refactor) Move generateShareId to locationActions and apply to LocationCard
- Moved generateShareId from shareChecksum.ts to locationActions.ts as a server action - Updated LocationCard to use shareID with checksum for proof of payment download link - Replaced Link with AsyncLink to handle async shareID generation - Commented out debug console.log in Pdf417Barcode Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ import { gotoHomeWithMessage } from './navigationActions';
|
|||||||
import { unstable_noStore, revalidatePath } from 'next/cache';
|
import { unstable_noStore, revalidatePath } from 'next/cache';
|
||||||
import { IntlTemplateFn } from '@/app/i18n';
|
import { IntlTemplateFn } from '@/app/i18n';
|
||||||
import { getTranslations, getLocale } from "next-intl/server";
|
import { getTranslations, getLocale } from "next-intl/server";
|
||||||
import { generateShareId, extractShareId, validateShareChecksum } from '../shareChecksum';
|
import { extractShareId, validateShareChecksum, generateShareChecksum } from '../shareChecksum';
|
||||||
import { validatePdfFile } from '../validators/pdfValidator';
|
import { validatePdfFile } from '../validators/pdfValidator';
|
||||||
import { checkUploadRateLimit } from '../uploadRateLimiter';
|
import { checkUploadRateLimit } from '../uploadRateLimiter';
|
||||||
|
|
||||||
@@ -765,6 +765,18 @@ export const uploadUtilBillsProofOfPayment = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate combined location ID with checksum appended
|
||||||
|
* @param locationId - The MongoDB location ID (24 chars)
|
||||||
|
* @returns Combined ID: locationId + checksum (40 chars total)
|
||||||
|
*/
|
||||||
|
export async function generateShareId(locationId: string): Promise<string> {
|
||||||
|
const checksum = generateShareChecksum(locationId);
|
||||||
|
return locationId + checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate/activate share link for location
|
* Generate/activate share link for location
|
||||||
* Called when owner clicks "Share" button
|
* Called when owner clicks "Share" button
|
||||||
@@ -800,7 +812,7 @@ export const generateShareLink = withUser(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Generate combined share ID (locationId + checksum)
|
// Generate combined share ID (locationId + checksum)
|
||||||
const shareId = generateShareId(locationId);
|
const shareId = await generateShareId(locationId);
|
||||||
|
|
||||||
// Build share URL
|
// Build share URL
|
||||||
const baseUrl = process.env.NEXTAUTH_URL || 'http://localhost:3000';
|
const baseUrl = process.env.NEXTAUTH_URL || 'http://localhost:3000';
|
||||||
|
|||||||
@@ -56,16 +56,6 @@ export function validateShareChecksum(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate combined location ID with checksum appended
|
|
||||||
* @param locationId - The MongoDB location ID (24 chars)
|
|
||||||
* @returns Combined ID: locationId + checksum (40 chars total)
|
|
||||||
*/
|
|
||||||
export function generateShareId(locationId: string): string {
|
|
||||||
const checksum = generateShareChecksum(locationId);
|
|
||||||
return locationId + checksum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract location ID and checksum from combined share ID
|
* Extract location ID and checksum from combined share ID
|
||||||
* @param shareId - Combined ID (locationId + checksum)
|
* @param shareId - Combined ID (locationId + checksum)
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { CheckCircleIcon, Cog8ToothIcon, PlusCircleIcon, ShareIcon, BanknotesIcon, EyeIcon, TicketIcon, ShoppingCartIcon, EnvelopeIcon, ExclamationTriangleIcon, ClockIcon } from "@heroicons/react/24/outline";
|
import { CheckCircleIcon, Cog8ToothIcon, PlusCircleIcon, ShareIcon, BanknotesIcon, EyeIcon, TicketIcon, ShoppingCartIcon, EnvelopeIcon, ExclamationTriangleIcon, ClockIcon } from "@heroicons/react/24/outline";
|
||||||
import { FC } from "react";
|
import { FC, useEffect, useState } from "react";
|
||||||
import { BillBadge } from "./BillBadge";
|
import { BillBadge } from "./BillBadge";
|
||||||
import { BillingLocation, EmailStatus } from "../lib/db-types";
|
import { BillingLocation, EmailStatus } from "../lib/db-types";
|
||||||
import { formatYearMonth } from "../lib/format";
|
import { formatYearMonth } from "../lib/format";
|
||||||
import { formatCurrency } from "../lib/formatStrings";
|
import { formatCurrency } from "../lib/formatStrings";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useLocale, useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { generateShareLink } from "../lib/actions/locationActions";
|
import { generateShareId, generateShareLink } from "../lib/actions/locationActions";
|
||||||
|
import { AsyncLink } from "./AsyncLink";
|
||||||
|
|
||||||
export interface LocationCardProps {
|
export interface LocationCardProps {
|
||||||
location: BillingLocation;
|
location: BillingLocation;
|
||||||
@@ -35,6 +36,18 @@ export const LocationCard: FC<LocationCardProps> = ({ location, currency }) => {
|
|||||||
const totalUnpaid = bills.reduce((acc, bill) => !bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0);
|
const totalUnpaid = bills.reduce((acc, bill) => !bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0);
|
||||||
const totalPayed = bills.reduce((acc, bill) => bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0);
|
const totalPayed = bills.reduce((acc, bill) => bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Share ID which can be used in shareable links
|
||||||
|
* Note: not to be used in share button directly since `generateShareLink` sets sharing TTL in the DB
|
||||||
|
* */
|
||||||
|
const [shareID, setShareID] = useState<string>("not-yet-generated");
|
||||||
|
|
||||||
|
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(_id)))();
|
||||||
|
}, [_id]);
|
||||||
|
|
||||||
const handleCopyLinkClick = async () => {
|
const handleCopyLinkClick = async () => {
|
||||||
// copy URL to clipboard
|
// copy URL to clipboard
|
||||||
const shareLink = await generateShareLink(_id);
|
const shareLink = await generateShareLink(_id);
|
||||||
@@ -124,16 +137,17 @@ export const LocationCard: FC<LocationCardProps> = ({ location, currency }) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{utilBillsProofOfPayment?.uploadedAt && (
|
{utilBillsProofOfPayment?.uploadedAt && (
|
||||||
<Link
|
<AsyncLink
|
||||||
href={`/share/proof-of-payment/${_id}/`}
|
href={`/share/proof-of-payment/combined/${shareID}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="flex mt-1 ml-1">
|
className="flex mt-1 ml-1"
|
||||||
|
disabled={!shareID} >
|
||||||
<span className="w-5 min-w-5 mr-2"><TicketIcon className="mt-[.1rem]" /></span>
|
<span className="w-5 min-w-5 mr-2"><TicketIcon className="mt-[.1rem]" /></span>
|
||||||
<span>
|
<span>
|
||||||
<span className="underline">{t("download-proof-of-payment-label")}</span>
|
<span className="underline">{t("download-proof-of-payment-label")}</span>
|
||||||
<CheckCircleIcon className="h-5 w-5 ml-2 mt-[-.2rem] text-success inline-block" />
|
<CheckCircleIcon className="h-5 w-5 ml-2 mt-[-.2rem] text-success inline-block" />
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</AsyncLink>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { renderBarcode } from '../lib/pdf/renderBarcode';
|
|||||||
export const Pdf417Barcode:FC<{hub3aText:string, className?: string }> = ({ hub3aText: hub3a_text, className }) => {
|
export const Pdf417Barcode:FC<{hub3aText:string, className?: string }> = ({ hub3aText: hub3a_text, className }) => {
|
||||||
const [bitmapData, setBitmapData] = useState<string | undefined>(undefined);
|
const [bitmapData, setBitmapData] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
console.log("Rendering Pdf417Barcode with hub3a_text:", hub3a_text);
|
// console.log("Rendering Pdf417Barcode with hub3a_text:", hub3a_text);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const aspectRatio = 3;
|
const aspectRatio = 3;
|
||||||
|
|||||||
Reference in New Issue
Block a user