diff --git a/app/attachment/[id]/route.tsx b/app/attachment/[id]/route.tsx index 7485ecc..258957c 100644 --- a/app/attachment/[id]/route.tsx +++ b/app/attachment/[id]/route.tsx @@ -1,4 +1,4 @@ -import { fetchBillById } from '@/app/lib/billActions'; +import { fetchBillById } from '@/app/lib/actions/billActions'; import { notFound } from 'next/navigation'; export async function GET(request: Request, { params:{ id } }: { params: { id:string } }) { diff --git a/app/bill/[id]/delete/page.tsx b/app/bill/[id]/delete/page.tsx index 311a0ed..3d41f02 100644 --- a/app/bill/[id]/delete/page.tsx +++ b/app/bill/[id]/delete/page.tsx @@ -1,4 +1,4 @@ -import { deleteBillById } from '@/app/lib/billActions'; +import { deleteBillById } from '@/app/lib/actions/billActions'; import { revalidatePath } from 'next/cache'; import { notFound, redirect } from 'next/navigation'; diff --git a/app/bill/[id]/edit/page.tsx b/app/bill/[id]/edit/page.tsx index ad2a4d3..c528641 100644 --- a/app/bill/[id]/edit/page.tsx +++ b/app/bill/[id]/edit/page.tsx @@ -1,5 +1,5 @@ import { BillingLocation, Bill } from '@/app/lib/db-types'; -import { fetchBillById } from '@/app/lib/billActions'; +import { fetchBillById } from '@/app/lib/actions/billActions'; import clientPromise from '@/app/lib/mongodb'; import { BillEditForm } from '@/app/ui/BillEditForm'; import { ObjectId } from 'mongodb'; diff --git a/app/lib/billActions.ts b/app/lib/actions/billActions.ts similarity index 90% rename from app/lib/billActions.ts rename to app/lib/actions/billActions.ts index 40bb27f..e3b47a4 100644 --- a/app/lib/billActions.ts +++ b/app/lib/actions/billActions.ts @@ -3,11 +3,11 @@ 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 clientPromise, { getDbClient } from '../dbClient'; +import { BillAttachment, BillingLocation } from '../db-types'; import { ObjectId } from 'mongodb'; import { withUser } from '@/app/lib/auth'; -import { AuthenticatedUser } from './types/next-auth'; +import { AuthenticatedUser } from '../types/next-auth'; export type State = { errors?: { @@ -140,8 +140,7 @@ export const updateOrAddBill = withUser(async (user:AuthenticatedUser, locationI const billPaid = formData.get('billPaid') === 'on'; // update the bill in the mongodb - const client = await clientPromise; - const db = client.db("rezije"); + const dbClient = await getDbClient(); const billAttachment = await serializeAttachment(formData.get('billAttachment') as File); @@ -163,7 +162,7 @@ export const updateOrAddBill = withUser(async (user:AuthenticatedUser, locationI }; // find a location with the given locationID - const post = await db.collection("lokacije").updateOne( + const post = await dbClient.collection("lokacije").updateOne( { _id: locationId, // find a location with the given locationID userId // make sure that the location belongs to the user @@ -177,7 +176,7 @@ export const updateOrAddBill = withUser(async (user:AuthenticatedUser, locationI }); } else { // find a location with the given locationID - const post = await db.collection("lokacije").updateOne( + const post = await dbClient.collection("lokacije").updateOne( { _id: locationId, // find a location with the given locationID userId // make sure that the location belongs to the user @@ -211,11 +210,10 @@ export const fetchBillById = withUser(async (user:AuthenticatedUser, locationID: const { id: userId } = user; - const client = await clientPromise; - const db = client.db("rezije"); + const dbClient = await getDbClient(); // find a location with the given locationID - const billLocation = await db.collection("lokacije").findOne({ _id: locationID, userId }) + const billLocation = await dbClient.collection("lokacije").findOne({ _id: locationID, userId }) if(!billLocation) { console.log(`Location ${locationID} not found`); @@ -237,11 +235,10 @@ export const deleteBillById = withUser(async (user:AuthenticatedUser, locationID const { id: userId } = user; - const client = await clientPromise; - const db = client.db("rezije"); + const dbClient = await getDbClient(); // find a location with the given locationID - const post = await db.collection("lokacije").updateOne( + const post = await dbClient.collection("lokacije").updateOne( { _id: locationID, // find a location with the given locationID userId // make sure that the location belongs to the user diff --git a/app/lib/locationActions.ts b/app/lib/actions/locationActions.ts similarity index 77% rename from app/lib/locationActions.ts rename to app/lib/actions/locationActions.ts index 2f68180..a06c6b2 100644 --- a/app/lib/locationActions.ts +++ b/app/lib/actions/locationActions.ts @@ -3,11 +3,11 @@ import { z } from 'zod'; import { revalidatePath } from 'next/cache'; import { redirect } from 'next/navigation'; -import clientPromise from './mongodb'; -import { BillingLocation } from './db-types'; +import clientPromise, { getDbClient } from '../dbClient'; +import { BillingLocation } from '../db-types'; import { ObjectId } from 'mongodb'; import { auth, withUser } from '@/app/lib/auth'; -import { AuthenticatedUser } from './types/next-auth'; +import { AuthenticatedUser } from '../types/next-auth'; import { NormalizedRouteManifest } from 'next/dist/server/base-server'; export type State = { @@ -54,13 +54,12 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat } = validatedFields.data; // update the bill in the mongodb - const client = await clientPromise; - const db = client.db("rezije"); + const dbClient = await getDbClient(); const { id: userId, email: userEmail } = user; if(locationId) { - await db.collection("lokacije").updateOne( + await dbClient.collection("lokacije").updateOne( { _id: locationId, // find a location with the given locationID userId // make sure the location belongs to the user @@ -72,7 +71,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat } }); } else if(yearMonth) { - await db.collection("lokacije").insertOne({ + await dbClient.collection("lokacije").insertOne({ _id: (new ObjectId()).toHexString(), userId, userEmail, @@ -90,17 +89,16 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat }); -export const fetchAllLocations = withUser(async (user:AuthenticatedUser, pageIx:number=0, pageSize:number=20) => { +export const fetchAllLocations = withUser(async (user:AuthenticatedUser, pageIx:number=0, pageSize:number=2000) => { - const client = await clientPromise; - const db = client.db("rezije"); + const dbClient = await getDbClient(); const { id: userId } = user; // fetch `pageSize` locations for the given page index - const locations = await db.collection("lokacije") + const locations = await dbClient.collection("lokacije") .find({ userId }) - .sort({ name: 1 }) + .sort({ yearMonth: -1, name: 1 }) .skip(pageIx * pageSize) .limit(pageSize) .toArray(); @@ -110,13 +108,12 @@ export const fetchAllLocations = withUser(async (user:AuthenticatedUser, pageIx: export const fetchLocationById = withUser(async (user:AuthenticatedUser, locationID:string) => { - const client = await clientPromise; - const db = client.db("rezije"); + const dbClient = await getDbClient(); const { id: userId } = user; // find a location with the given locationID - const billLocation = await db.collection("lokacije").findOne({ _id: locationID, userId}); + const billLocation = await dbClient.collection("lokacije").findOne({ _id: locationID, userId}); if(!billLocation) { console.log(`Location ${locationID} not found`); @@ -128,12 +125,12 @@ export const fetchLocationById = withUser(async (user:AuthenticatedUser, locatio export const deleteLocationById = withUser(async (user:AuthenticatedUser, locationID:string) => { - const client = await clientPromise; - const db = client.db("rezije"); + const dbClient = await getDbClient(); + const { id: userId } = user; // find a location with the given locationID - const post = await db.collection("lokacije").deleteOne({ _id: locationID, userId }); + const post = await dbClient.collection("lokacije").deleteOne({ _id: locationID, userId }); return(post.deletedCount); }) \ No newline at end of file diff --git a/app/lib/yearMonthActions.ts b/app/lib/actions/yearMonthActions.ts similarity index 80% rename from app/lib/yearMonthActions.ts rename to app/lib/actions/yearMonthActions.ts index 6bb6b1a..21cc856 100644 --- a/app/lib/yearMonthActions.ts +++ b/app/lib/actions/yearMonthActions.ts @@ -2,11 +2,11 @@ import { revalidatePath } from 'next/cache'; import { redirect } from 'next/navigation'; -import clientPromise from './mongodb'; +import clientPromise, { getDbClient } from '../dbClient'; import { ObjectId } from 'mongodb'; -import { BillingLocation } from './db-types'; -import { AuthenticatedUser } from './types/next-auth'; -import { withUser } from './auth'; +import { BillingLocation } from '../db-types'; +import { AuthenticatedUser } from '../types/next-auth'; +import { withUser } from '../auth'; /** * Server-side action which adds a new month to the database @@ -20,14 +20,14 @@ export const addYearMonth = withUser(async (user:AuthenticatedUser, yearMonthStr const { id: userId } = user; // update the bill in the mongodb - const client = await clientPromise; - const db = client.db("rezije"); + const dbClient = await getDbClient(); + const yearMonth = parseInt(yearMonthString); const prevYearMonth = (yearMonth - 1) % 100 === 0 ? yearMonth - 89 : yearMonth - 1; // find all locations for the previous month - const prevMonthLocations = await db.collection("lokacije").find({ + const prevMonthLocations = await dbClient.collection("lokacije").find({ userId, // make sure that the locations belongs to the user yearMonth: prevYearMonth }); @@ -52,7 +52,7 @@ export const addYearMonth = withUser(async (user:AuthenticatedUser, yearMonthStr }); const newMonthLocations = await newMonthLocationsCursor.toArray() - await db.collection("lokacije").insertMany(newMonthLocations); + await dbClient.collection("lokacije").insertMany(newMonthLocations); // clear the cache for the path revalidatePath('/'); @@ -67,11 +67,10 @@ export async function gotoHome() { export const fetchBillById = withUser(async (user:AuthenticatedUser, locationID:string, billID:string) => { const { id: userId } = user; - const client = await clientPromise; - const db = client.db("rezije"); + const dbClient = await getDbClient(); // find a location with the given locationID - const billLocation = await db.collection("lokacije").findOne({ + const billLocation = await dbClient.collection("lokacije").findOne({ _id: locationID, userId // make sure that the location belongs to the user }) @@ -95,11 +94,10 @@ export const fetchBillById = withUser(async (user:AuthenticatedUser, locationID: export const deleteBillById = withUser(async (user:AuthenticatedUser, locationID:string, billID:string) => { const { id: userId } = user; - const client = await clientPromise; - const db = client.db("rezije"); + const dbClient = await getDbClient(); // find a location with the given locationID - const post = await db.collection("lokacije").updateOne( + const post = await dbClient.collection("lokacije").updateOne( { _id: locationID, // find a location with the given locationID userId // make sure that the location belongs to the user diff --git a/app/lib/mongodb.ts b/app/lib/dbClient.ts similarity index 86% rename from app/lib/mongodb.ts rename to app/lib/dbClient.ts index 1bcc3cc..94bd145 100644 --- a/app/lib/mongodb.ts +++ b/app/lib/dbClient.ts @@ -31,4 +31,10 @@ if (process.env.NODE_ENV === 'development') { // Export a module-scoped MongoClient promise. By doing this in a // separate module, the client can be shared across functions. -export default clientPromise \ No newline at end of file +export default clientPromise + +export const getDbClient = async () => { + const client = await clientPromise; + const db = client.db("rezije"); + return(db); +} diff --git a/app/location/[id]/delete/page.tsx b/app/location/[id]/delete/page.tsx index 9344ac2..687570b 100644 --- a/app/location/[id]/delete/page.tsx +++ b/app/location/[id]/delete/page.tsx @@ -1,5 +1,5 @@ -import { deleteBillById } from '@/app/lib/billActions'; -import { deleteLocationById } from '@/app/lib/locationActions'; +import { deleteBillById } from '@/app/lib/actions/billActions'; +import { deleteLocationById } from '@/app/lib/actions/locationActions'; import { revalidatePath } from 'next/cache'; import { notFound, redirect } from 'next/navigation'; diff --git a/app/location/[id]/edit/page.tsx b/app/location/[id]/edit/page.tsx index d2b7360..4a6c6a8 100644 --- a/app/location/[id]/edit/page.tsx +++ b/app/location/[id]/edit/page.tsx @@ -1,11 +1,11 @@ import { BillingLocation, Bill } from '@/app/lib/db-types'; -import { fetchBillById } from '@/app/lib/billActions'; +import { fetchBillById } from '@/app/lib/actions/billActions'; import clientPromise from '@/app/lib/mongodb'; import { BillEditForm } from '@/app/ui/BillEditForm'; import { ObjectId } from 'mongodb'; import { notFound } from 'next/navigation'; import { LocationEditForm } from '@/app/ui/LocationEditForm'; -import { fetchLocationById } from '@/app/lib/locationActions'; +import { fetchLocationById } from '@/app/lib/actions/locationActions'; export default async function Page({ params:{ id } }: { params: { id:string } }) { diff --git a/app/page.tsx b/app/page.tsx index 3d11757..3271209 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -4,7 +4,7 @@ import { AddMonthButton } from './ui/AddMonthButton'; import { AddLocationButton } from './ui/AddLocationButton'; import { PageFooter } from './ui/PageFooter'; import { isAuthErrorMessage } from '@/app/lib/auth'; -import { fetchAllLocations } from './lib/locationActions'; +import { fetchAllLocations } from './lib/actions/locationActions'; import { formatCurrency } from './lib/formatStrings'; const getNextYearMonth = (yearMonth:number) => { diff --git a/app/ui/BillEditForm.tsx b/app/ui/BillEditForm.tsx index 6acb84f..a227f0c 100644 --- a/app/ui/BillEditForm.tsx +++ b/app/ui/BillEditForm.tsx @@ -4,7 +4,7 @@ import { DocumentIcon, TrashIcon } from "@heroicons/react/24/outline"; import { Bill } from "../lib/db-types"; import React, { FC } from "react"; import { useFormState } from "react-dom"; -import { gotoHome, updateOrAddBill } from "../lib/billActions"; +import { gotoHome, updateOrAddBill } from "../lib/actions/billActions"; // Next.js does not encode an utf-8 file name correctly when sending a form with a file attachment // This is a workaround for that diff --git a/app/ui/LocationEditForm.tsx b/app/ui/LocationEditForm.tsx index a2f55e1..0cd2a79 100644 --- a/app/ui/LocationEditForm.tsx +++ b/app/ui/LocationEditForm.tsx @@ -3,9 +3,9 @@ import { TrashIcon } from "@heroicons/react/24/outline"; import { FC } from "react"; import { BillingLocation } from "../lib/db-types"; -import { updateOrAddLocation } from "../lib/locationActions"; +import { updateOrAddLocation } from "../lib/actions/locationActions"; import { useFormState } from "react-dom"; -import { gotoHome } from "../lib/billActions"; +import { gotoHome } from "../lib/actions/billActions"; export interface LocationEditFormProps { /** location which should be edited */ diff --git a/app/year-month/[id]/add/page.tsx b/app/year-month/[id]/add/page.tsx index b5a43ec..fef23e4 100644 --- a/app/year-month/[id]/add/page.tsx +++ b/app/year-month/[id]/add/page.tsx @@ -1,4 +1,4 @@ -import { addYearMonth } from '@/app/lib/yearMonthActions'; +import { addYearMonth } from '@/app/lib/actions/yearMonthActions'; import { revalidatePath } from 'next/cache'; import { redirect } from 'next/navigation';