'use server'; import { z } from 'zod'; import { revalidatePath } from 'next/cache'; import { redirect } from 'next/navigation'; import clientPromise from './mongodb'; import { BillAttachment, BillingLocation } from './db-types'; import { ObjectId } from 'mongodb'; export type State = { errors?: { billName?: string[]; billAttachment?: string[], billNotes?: string[], }; message?:string | null; } const FormSchema = z.object({ _id: z.string(), billName: z.coerce.string().min(1, "Bill Name is required."), billNotes: z.string(), }); const UpdateBill = FormSchema.omit({ _id: true }); /** * converts the file to a format stored in the database * @param billAttachment * @returns */ const serializeAttachment = async (billAttachment: File | null) => { if (!billAttachment) { return null; } const { name: fileName, size: fileSize, type: fileType, lastModified: fileLastModified, } = billAttachment; if(!fileName || fileName === 'undefined') { return null; } // convert the billAttachment file contents to format that can be stored in the database const fileContents = await billAttachment.arrayBuffer(); const fileContentsBase64 = Buffer.from(fileContents).toString('base64'); // create an object to store the file in the database return({ fileName, fileSize, fileType, fileLastModified, fileContentsBase64, } as BillAttachment); } /** * Server-side action which adds or updates a bill * @param locationId location of the bill * @param billId ID of the bill * @param prevState previous state of the form * @param formData form data * @returns */ export async function updateOrAddBill(locationId: string, billId?:string, prevState:State, formData: FormData) { const validatedFields = UpdateBill.safeParse({ billName: formData.get('billName'), billNotes: formData.get('billNotes'), }); // If form validation fails, return errors early. Otherwise, continue... if(!validatedFields.success) { console.log("updateBill.validation-error"); return({ errors: validatedFields.error.flatten().fieldErrors, message: "Missing Fields. Field to Update Bill.", }); } const { billName, billNotes, } = validatedFields.data; const billPaid = formData.get('billPaid') === 'on'; // update the bill in the mongodb const client = await clientPromise; const db = client.db("rezije"); const billAttachment = await serializeAttachment(formData.get('billAttachment') as File); // if there is an attachment, update the attachment field // otherwise, do not update the attachment field const mongoDbSet = billAttachment ? { "bills.$[elem].name": billName, "bills.$[elem].paid": billPaid, "bills.$[elem].attachment": billAttachment, "bills.$[elem].notes": billNotes, }: { "bills.$[elem].name": billName, "bills.$[elem].paid": billPaid, "bills.$[elem].notes": billNotes, }; if(billId) { // find a location with the given locationID const post = await db.collection("lokacije").updateOne( { _id: locationId // find a location with the given locationID }, { $set: mongoDbSet }, { arrayFilters: [ { "elem._id": { $eq: billId } } // find a bill with the given billID ] }); } else { // find a location with the given locationID const post = await db.collection("lokacije").updateOne( { _id: locationId // find a location with the given locationID }, { $push: { bills: { _id: (new ObjectId()).toHexString(), name: billName, paid: billPaid, attachment: billAttachment, notes: billNotes, } } }); } // clear the cache for the path revalidatePath('/'); // go to the bill list redirect('/'); } export async function gotoHome() { revalidatePath('/'); redirect('/'); } export const fetchBillById = async (locationID:string, billID:string) => { const client = await clientPromise; const db = client.db("rezije"); // find a location with the given locationID const billLocation = await db.collection("lokacije").findOne({ _id: locationID }) if(!billLocation) { console.log(`Location ${locationID} not found`); return(null); } // find a bill with the given billID const bill = billLocation?.bills.find(({ _id }) => _id.toString() === billID); if(!bill) { console.log('Bill not found'); return(null); } return(bill); } export const deleteBillById = async (locationID:string, billID:string) => { const client = await clientPromise; const db = client.db("rezije"); // find a location with the given locationID const post = await db.collection("lokacije").updateOne( { _id: locationID // find a location with the given locationID }, { // remove the bill with the given billID $pull: { bills: { _id: billID } } }); return(post.modifiedCount); }