From 79dbe04245fb79f846bf63ea52ec8ce4bc45f0f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Thu, 4 Jan 2024 16:25:22 +0100 Subject: [PATCH] implemented bill update --- app/bills/[id]/edit/page.tsx | 26 +++++++++++--- app/lib/actions.ts | 67 ++++++++++++++++++++++++++++++++++++ app/lib/db-types.ts | 32 ++++++++++++++--- app/ui/BillEditForm.tsx | 65 +++++++++++++++++++--------------- 4 files changed, 155 insertions(+), 35 deletions(-) diff --git a/app/bills/[id]/edit/page.tsx b/app/bills/[id]/edit/page.tsx index 185a625..85a120b 100644 --- a/app/bills/[id]/edit/page.tsx +++ b/app/bills/[id]/edit/page.tsx @@ -1,4 +1,4 @@ -import { Location } from '@/app/lib/db-types'; +import { PlainLocation, PlainBill, MongoLocation } from '@/app/lib/db-types'; import clientPromise from '@/app/lib/mongodb'; import { BillEditForm } from '@/app/ui/BillEditForm'; import { ObjectId } from 'mongodb'; @@ -9,9 +9,28 @@ const fetchBillById = async (locationID:string, billID:string) => { const db = client.db("rezije"); // find a location with the given locationID - const billLocation = await db.collection("lokacije").findOne({ _id: 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 - return(billLocation?.bills.find(({ _id }) => _id.toString() === 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); + } export default async function Page({ params:{ id } }: { params: { id:string } }) { @@ -21,7 +40,6 @@ export default async function Page({ params:{ id } }: { params: { id:string } }) const bill = await fetchBillById(invoiceID, billID); if (!bill) { - console.log('Bill not found'); notFound(); } return ( diff --git a/app/lib/actions.ts b/app/lib/actions.ts index 5a4d656..bd5b7c2 100644 --- a/app/lib/actions.ts +++ b/app/lib/actions.ts @@ -1,2 +1,69 @@ 'use server'; +import { z } from 'zod'; +import { revalidatePath } from 'next/cache'; +import { redirect } from 'next/navigation'; +import clientPromise from './mongodb'; +import { ObjectId } from 'mongodb'; + +export type State = { + errors?: { + billName?: string[]; + }; + message?:string | null; +} + +const FormSchema = z.object({ + _id: z.string(), + billName: z.string({ + invalid_type_error: 'Please select a bill name', + }), + }); + +const UpdateBill = FormSchema.omit({ _id: true }); + +export async function updateBill(locationId: string, billId:string, prevState:State, formData: FormData) { + + const validatedFields = UpdateBill.safeParse({ + billName: formData.get('billName'), + }); + + // 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, + } = validatedFields.data; + + // update the bill in the mongodb + 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 + }, + { + $set: { + "bills.$[elem].name": billName, + } + }, { + arrayFilters: [ + { "elem._id": { $eq: new ObjectId(billId) } } // find a bill with the given billID + ] + }); + + console.log("updateBill.success", post); + + // clear the cache for the path + revalidatePath('/'); + // go to the bill list + redirect('/'); +} \ No newline at end of file diff --git a/app/lib/db-types.ts b/app/lib/db-types.ts index 5c8506a..9bf4350 100644 --- a/app/lib/db-types.ts +++ b/app/lib/db-types.ts @@ -1,16 +1,40 @@ import { ObjectId } from "mongodb"; -export interface Location { - _id: string; +/** Bill basic data */ +export interface LocationBase { name: string; - bills: Bill[]; /** the value is encoded as yyyymm (i.e. 202301) */ yearMonth: number; }; -export interface 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 { name: string; paid: boolean; document?: 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; +}; diff --git a/app/ui/BillEditForm.tsx b/app/ui/BillEditForm.tsx index 1546144..4ff2c10 100644 --- a/app/ui/BillEditForm.tsx +++ b/app/ui/BillEditForm.tsx @@ -1,35 +1,46 @@ -import { Cog8ToothIcon, DocumentIcon, TrashIcon } from "@heroicons/react/24/outline"; -import { Bill } from "../lib/db-types"; +"use client"; + +import { TrashIcon } from "@heroicons/react/24/outline"; +import { PlainBill } from "../lib/db-types"; import { FC } from "react"; -import { ObjectId } from "mongodb"; +import { useFormState } from "react-dom"; +import { updateBill } from "../lib/actions"; + export interface BillEditFormProps { invoiceID: string, - bill: Bill + bill: PlainBill } -export const BillEditForm:FC = ({ bill: { name, paid } }) => +export const BillEditForm:FC = ({ invoiceID, bill: { id, name, paid } }) => { + + const initialState = { message: null, errors: {} }; + const updateBillWithId = updateBill.bind(null, invoiceID, id); + const [ state, dispatch ] = useFormState(updateBillWithId, initialState); + + return(
-
-
- - - { - // - // - // - // 2023-22-12 document GSKG račun za 2023.pdf - // - } - -
- +
+ + + + { + // + // + // + // 2023-22-12 document GSKG račun za 2023.pdf + // + } + +
+ +
+ + +
- - - -
-
\ No newline at end of file +
); +} \ No newline at end of file