From 86135199a93433a4c9ae689db62d8fb2485d6762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Fri, 5 Jan 2024 13:54:25 +0100 Subject: [PATCH] implemented bill deletion + var rename --- app/attachment/[id]/not-found.tsx | 20 ++-------- app/attachment/[id]/route.tsx | 6 +-- app/bills/[id]/delete/not-found.tsx | 4 ++ app/bills/[id]/delete/page.tsx | 12 ++++++ app/bills/[id]/edit/not-found.tsx | 20 ++-------- app/bills/[id]/edit/page.tsx | 12 +++--- app/lib/actions.ts | 57 +++++++++++++++++++++++++++-- app/lib/db-types.ts | 36 ++++-------------- app/lib/fetchBillById.ts | 33 +---------------- app/ui/BillBadge.tsx | 5 +-- app/ui/BillEditForm.tsx | 16 ++++---- app/ui/NotFoundPage.tsx | 18 +++++++++ 12 files changed, 122 insertions(+), 117 deletions(-) create mode 100644 app/bills/[id]/delete/not-found.tsx create mode 100644 app/bills/[id]/delete/page.tsx create mode 100644 app/ui/NotFoundPage.tsx diff --git a/app/attachment/[id]/not-found.tsx b/app/attachment/[id]/not-found.tsx index b38ad72..aad21c3 100644 --- a/app/attachment/[id]/not-found.tsx +++ b/app/attachment/[id]/not-found.tsx @@ -1,18 +1,4 @@ -import Link from 'next/link'; -import { FaceFrownIcon } from '@heroicons/react/24/outline'; +import { NotFoundPage } from '@/app/ui/NotFoundPage'; -export default function NotFound() { - return ( -
- -

404 File Not Found

-

Could not find the requested attachment.

- - Go Back - -
- ); -} \ No newline at end of file +export default () => +; \ No newline at end of file diff --git a/app/attachment/[id]/route.tsx b/app/attachment/[id]/route.tsx index 866b557..63bcb38 100644 --- a/app/attachment/[id]/route.tsx +++ b/app/attachment/[id]/route.tsx @@ -1,10 +1,10 @@ -import { fetchBillById } from '@/app/lib/fetchBillById'; +import { fetchBillById } from '@/app/lib/actions'; import { notFound } from 'next/navigation'; export async function GET(request: Request, { params:{ id } }: { params: { id:string } }) { - const [invoiceID, billID] = id.split('-'); + const [locationID, billID] = id.split('-'); - const bill = await fetchBillById(invoiceID, billID); + const bill = await fetchBillById(locationID, billID); if(!bill?.attachment) { notFound(); diff --git a/app/bills/[id]/delete/not-found.tsx b/app/bills/[id]/delete/not-found.tsx new file mode 100644 index 0000000..483cd2e --- /dev/null +++ b/app/bills/[id]/delete/not-found.tsx @@ -0,0 +1,4 @@ +import { NotFoundPage } from '@/app/ui/NotFoundPage'; + +export default () => +; \ No newline at end of file diff --git a/app/bills/[id]/delete/page.tsx b/app/bills/[id]/delete/page.tsx new file mode 100644 index 0000000..f24081e --- /dev/null +++ b/app/bills/[id]/delete/page.tsx @@ -0,0 +1,12 @@ +import { deleteBillById } from '@/app/lib/actions'; +import { notFound, redirect } from 'next/navigation'; + +export default async function Page({ params:{ id } }: { params: { id:string } }) { + const [locationID, billID] = id.split('-'); + + if(await deleteBillById(locationID, billID) === 0) { + return(notFound()); + } + + redirect(`/`); +} \ No newline at end of file diff --git a/app/bills/[id]/edit/not-found.tsx b/app/bills/[id]/edit/not-found.tsx index 2475d26..483cd2e 100644 --- a/app/bills/[id]/edit/not-found.tsx +++ b/app/bills/[id]/edit/not-found.tsx @@ -1,18 +1,4 @@ -import Link from 'next/link'; -import { FaceFrownIcon } from '@heroicons/react/24/outline'; +import { NotFoundPage } from '@/app/ui/NotFoundPage'; -export default function NotFound() { - return ( -
- -

404 Bill Not Found

-

Could not find the requested Bill.

- - Go Back - -
- ); -} \ No newline at end of file +export default () => +; \ No newline at end of file diff --git a/app/bills/[id]/edit/page.tsx b/app/bills/[id]/edit/page.tsx index 1525e25..da69571 100644 --- a/app/bills/[id]/edit/page.tsx +++ b/app/bills/[id]/edit/page.tsx @@ -1,5 +1,5 @@ -import { PlainLocation, PlainBill, MongoLocation } from '@/app/lib/db-types'; -import { fetchBillById } from '@/app/lib/fetchBillById'; +import { BillingLocation, Bill } from '@/app/lib/db-types'; +import { fetchBillById } from '@/app/lib/actions'; import clientPromise from '@/app/lib/mongodb'; import { BillEditForm } from '@/app/ui/BillEditForm'; import { ObjectId } from 'mongodb'; @@ -7,16 +7,16 @@ import { notFound } from 'next/navigation'; export default async function Page({ params:{ id } }: { params: { id:string } }) { - const [invoiceID, billID] = id.split('-'); + const [locationID, billID] = id.split('-'); - const bill = await fetchBillById(invoiceID, billID); + const bill = await fetchBillById(locationID, billID); if (!bill) { - notFound(); + return(notFound()); } return (
- +
); } \ No newline at end of file diff --git a/app/lib/actions.ts b/app/lib/actions.ts index 4e152cf..8463227 100644 --- a/app/lib/actions.ts +++ b/app/lib/actions.ts @@ -4,8 +4,7 @@ import { z } from 'zod'; import { revalidatePath } from 'next/cache'; import { redirect } from 'next/navigation'; import clientPromise from './mongodb'; -import { ObjectId } from 'mongodb'; -import { BillAttachment } from './db-types'; +import { BillAttachment, Bill } from './db-types'; export type State = { errors?: { @@ -113,7 +112,7 @@ export async function updateBill(locationId: string, billId:string, prevState:St // find a location with the given locationID - const post = await db.collection("lokacije").updateOne( + const post = await db.collection("lokacije").updateOne( { _id: locationId // find a location with the given locationID }, @@ -121,7 +120,7 @@ export async function updateBill(locationId: string, billId:string, prevState:St $set: mongoDbSet }, { arrayFilters: [ - { "elem._id": { $eq: new ObjectId(billId) } } // find a bill with the given billID + { "elem._id": { $eq: billId } } // find a bill with the given billID ] }); @@ -133,4 +132,54 @@ export async function updateBill(locationId: string, billId:string, prevState:St export async function gotoHome() { 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); + } + + const { _id, ...billBase } = Bill; + + return({ + id: _id.toString(), + ...billBase + } as 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); } \ No newline at end of file diff --git a/app/lib/db-types.ts b/app/lib/db-types.ts index cbf1ec2..a0eb685 100644 --- a/app/lib/db-types.ts +++ b/app/lib/db-types.ts @@ -8,42 +8,20 @@ export interface BillAttachment { fileContentsBase64: string; }; -/** Bill basic data */ -export interface LocationBase { +/** bill object in the form returned by MongoDB */ +export interface BillingLocation { + _id: string; name: string; /** the value is encoded as yyyymm (i.e. 202301) */ yearMonth: number; + bills: Bill[]; }; - -/** bill object in the form returned by MongoDB */ -export interface MongoLocation { - _id: ObjectId; - bills: MongoBill[]; -}; - -/** plain-object Location version */ -export interface PlainLocation { - id: string; - bills: PlainBill[]; -}; - - /** Bill basic data */ -export interface BillBase { +export interface Bill { + _id: string; name: string; paid: boolean; attachment?: BillAttachment|null; notes?: string|null; -}; - -/** bill object in the form returned by MongoDB */ -export interface MongoBill extends BillBase { - _id: ObjectId; -}; - - -/** plain-object bill version */ -export interface PlainBill extends BillBase { - id: string; -}; +}; \ No newline at end of file diff --git a/app/lib/fetchBillById.ts b/app/lib/fetchBillById.ts index ea30b51..b17b72b 100644 --- a/app/lib/fetchBillById.ts +++ b/app/lib/fetchBillById.ts @@ -1,31 +1,2 @@ -import { PlainBill, MongoLocation } from '@/app/lib/db-types'; -import clientPromise from '@/app/lib/mongodb'; - -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 mongoBill = billLocation?.bills.find(({ _id }) => _id.toString() === billID); - - if(!mongoBill) { - console.log('Bill not found'); - return(null); - } - - const { _id, ...billBase } = mongoBill; - - return({ - id: _id.toString(), - ...billBase - } as PlainBill); - -} \ No newline at end of file +import { Bill, BillingLocation } from '@/app/lib/db-types'; +import clientPromise from '@/app/lib/mongodb'; \ No newline at end of file diff --git a/app/ui/BillBadge.tsx b/app/ui/BillBadge.tsx index 79b8f72..57e982f 100644 --- a/app/ui/BillBadge.tsx +++ b/app/ui/BillBadge.tsx @@ -1,10 +1,9 @@ import { FC } from "react" -import { Bill, Location } from "@/app/lib/db-types" +import { Bill } from "@/app/lib/db-types" import Link from "next/link" -import { ObjectId } from "mongodb"; export interface BillBadgeProps { - locationId: ObjectId, + locationId: string, bill: Bill }; diff --git a/app/ui/BillEditForm.tsx b/app/ui/BillEditForm.tsx index 3b61084..cc51f7d 100644 --- a/app/ui/BillEditForm.tsx +++ b/app/ui/BillEditForm.tsx @@ -1,7 +1,7 @@ "use client"; import { DocumentIcon, TrashIcon } from "@heroicons/react/24/outline"; -import { PlainBill } from "../lib/db-types"; +import { Bill } from "../lib/db-types"; import { FC } from "react"; import { useFormState } from "react-dom"; import { gotoHome, updateBill } from "../lib/actions"; @@ -20,14 +20,14 @@ const updateBill2 = (locationId: string, billId:string, prevState:any, formData: } export interface BillEditFormProps { - invoiceID: string, - bill: PlainBill + locationID: string, + bill: Bill } -export const BillEditForm:FC = ({ invoiceID, bill: { id, name, paid, attachment, notes } }) => { +export const BillEditForm:FC = ({ locationID, bill: { id, name, paid, attachment, notes } }) => { const initialState = { message: null, errors: {} }; - const updateBillWithId = updateBill2.bind(null, invoiceID, id); + const updateBillWithId = updateBill2.bind(null, locationID, id); const [ state, dispatch ] = useFormState(updateBillWithId, initialState); // redirect to the main page @@ -40,7 +40,9 @@ export const BillEditForm:FC = ({ invoiceID, bill: { id, name
- + + +
@@ -55,7 +57,7 @@ export const BillEditForm:FC = ({ invoiceID, bill: { id, name // attachment ? - + {decodeURIComponent(attachment.fileName)} diff --git a/app/ui/NotFoundPage.tsx b/app/ui/NotFoundPage.tsx new file mode 100644 index 0000000..95301b9 --- /dev/null +++ b/app/ui/NotFoundPage.tsx @@ -0,0 +1,18 @@ +import Link from 'next/link'; +import { FaceFrownIcon } from "@heroicons/react/24/outline"; +import { FC } from 'react'; + +export interface NotFoundPageProps { + title?:string, + description?:string, +} + +export const NotFoundPage:FC = ({ title="404 Not Found", description="Could not find the requested content." }) => +
+ +

{title}

+

{description}

+ + Go Back + +
\ No newline at end of file