Add utility bills proof of payment file upload functionality
Changes: - Updated BillingLocation interface: - Added utilBillsProofOfPaymentAttachment field (BillAttachment type) - Added server action uploadUtilBillsProofOfPayment: - Validates PDF file type - Serializes file attachment to base64 - Stores attachment in BillingLocation document - Returns success/error status - Updated ViewLocationCard component: - Added file upload input with PDF-only accept - Implemented handleFileChange with immediate upload - Added upload state management (isUploading, uploadError, attachment) - Shows spinner while uploading - Input disabled during upload - Conditionally renders file input or download link - Link displayed after successful upload - Created route handler for serving proof of payment PDFs: - GET /share/proof-of-payment/[id]/route.tsx - Fetches attachment from database - Converts base64 to binary - Returns PDF with proper headers - Added not-found page for proof of payment route - Updated middleware to include proof-of-payment in public pages - Added translations: - en: "Upload proof of payment (PDF only)" - hr: "Priložite potvrdu o uplati:" File uploads immediately on selection without page reload. Only PDF files accepted with client and server-side validation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -580,4 +580,77 @@ export const setSeenByTenant = async (locationID: string): Promise<void> => {
|
||||
{ _id: locationID },
|
||||
{ $set: { seenByTenant: true } }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a file attachment to be stored in the database
|
||||
* @param file - The file to serialize
|
||||
* @returns BillAttachment object or null if file is invalid
|
||||
*/
|
||||
const serializeAttachment = async (file: File | null) => {
|
||||
if (!file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
name: fileName,
|
||||
size: fileSize,
|
||||
type: fileType,
|
||||
lastModified: fileLastModified,
|
||||
} = file;
|
||||
|
||||
if(!fileName || fileName === 'undefined' || fileSize === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Convert file contents to base64 for database storage
|
||||
const fileContents = await file.arrayBuffer();
|
||||
const fileContentsBase64 = Buffer.from(fileContents).toString('base64');
|
||||
|
||||
return {
|
||||
fileName,
|
||||
fileSize,
|
||||
fileType,
|
||||
fileLastModified,
|
||||
fileContentsBase64,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads utility bills proof of payment attachment for a location
|
||||
* @param locationID - The ID of the location
|
||||
* @param formData - FormData containing the file
|
||||
* @returns Promise with success status
|
||||
*/
|
||||
export const uploadUtilBillsProofOfPayment = async (locationID: string, formData: FormData): Promise<{ success: boolean; error?: string }> => {
|
||||
noStore();
|
||||
|
||||
try {
|
||||
const file = formData.get('utilBillsProofOfPaymentAttachment') as File;
|
||||
|
||||
// Validate file type
|
||||
if (file && file.type !== 'application/pdf') {
|
||||
return { success: false, error: 'Only PDF files are accepted' };
|
||||
}
|
||||
|
||||
const attachment = await serializeAttachment(file);
|
||||
|
||||
if (!attachment) {
|
||||
return { success: false, error: 'Invalid file' };
|
||||
}
|
||||
|
||||
const dbClient = await getDbClient();
|
||||
|
||||
// Update the location with the attachment
|
||||
await dbClient.collection<BillingLocation>("lokacije")
|
||||
.updateOne(
|
||||
{ _id: locationID },
|
||||
{ $set: { utilBillsProofOfPaymentAttachment: attachment } }
|
||||
);
|
||||
|
||||
return { success: true };
|
||||
} catch (error: any) {
|
||||
console.error('Error uploading util bills proof of payment:', error);
|
||||
return { success: false, error: error.message || 'Upload failed' };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user