'use server'; import { z } from 'zod'; import { revalidatePath } from 'next/cache'; import { redirect } from 'next/navigation'; import clientPromise, { getDbClient } from '../dbClient'; import { BillingLocation, YearMonth } from '../db-types'; import { ObjectId } from 'mongodb'; import { auth, withUser } from '@/app/lib/auth'; import { AuthenticatedUser } from '../types/next-auth'; import { NormalizedRouteManifest } from 'next/dist/server/base-server'; export type State = { errors?: { locationName?: string[]; locationNotes?: string[], }; message?:string | null; } const FormSchema = z.object({ _id: z.string(), locationName: z.coerce.string().min(1, "Location Name is required."), locationNotes: z.string(), }); const UpdateLocation = FormSchema.omit({ _id: true }); /** * Server-side action which adds or updates a bill * @param locationId location of the bill * @param prevState previous state of the form * @param formData form data * @returns */ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locationId?: string, yearMonth?: YearMonth, prevState:State, formData: FormData) => { const validatedFields = UpdateLocation.safeParse({ locationName: formData.get('locationName'), locationNotes: formData.get('locationNotes'), }); // If form validation fails, return errors early. Otherwise, continue... if(!validatedFields.success) { return({ errors: validatedFields.error.flatten().fieldErrors, message: "Missing Fields", }); } const { locationName, locationNotes, } = validatedFields.data; // update the bill in the mongodb const dbClient = await getDbClient(); const { id: userId, email: userEmail } = user; if(locationId) { await dbClient.collection("lokacije").updateOne( { _id: locationId, // find a location with the given locationID userId // make sure the location belongs to the user }, { $set: { name: locationName, notes: locationNotes, } }); } else if(yearMonth) { await dbClient.collection("lokacije").insertOne({ _id: (new ObjectId()).toHexString(), userId, userEmail, name: locationName, notes: locationNotes, yearMonth: yearMonth, bills: [], }); } // clear the cache for the path revalidatePath('/'); // go to the bill list redirect('/'); }); export const fetchAllLocations = withUser(async (user:AuthenticatedUser, pageIx:number=0, pageSize:number=2000) => { const dbClient = await getDbClient(); const { id: userId } = user; // fetch `pageSize` locations for the given page index const locations = await dbClient.collection("lokacije") .find({ userId }) .sort({ "yearMonth.year": -1, "yearMonth.month": -1, name: 1, }) .skip(pageIx * pageSize) .limit(pageSize) .toArray(); return(locations); }) export const fetchLocationById = withUser(async (user:AuthenticatedUser, locationID:string) => { const dbClient = await getDbClient(); const { id: userId } = user; // find a location with the given locationID const billLocation = await dbClient.collection("lokacije").findOne({ _id: locationID, userId}); if(!billLocation) { console.log(`Location ${locationID} not found`); return(null); } return(billLocation); }) export const deleteLocationById = withUser(async (user:AuthenticatedUser, locationID:string) => { const dbClient = await getDbClient(); const { id: userId } = user; // find a location with the given locationID const post = await dbClient.collection("lokacije").deleteOne({ _id: locationID, userId }); return(post.deletedCount); })