From a3c7e8f4c9dcef13b2a94f8480af54805104931b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Tue, 6 Feb 2024 15:37:37 +0100 Subject: [PATCH 01/12] removing legacy code --- app/ui/BillEditForm.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/ui/BillEditForm.tsx b/app/ui/BillEditForm.tsx index 500ca12..b08e84c 100644 --- a/app/ui/BillEditForm.tsx +++ b/app/ui/BillEditForm.tsx @@ -6,7 +6,6 @@ import React, { FC } from "react"; import { useFormState } from "react-dom"; import { updateOrAddBill } from "../lib/actions/billActions"; import Link from "next/link"; -import { gotoHome } from "../lib/actions/navigationActions"; import { formatYearMonth } from "../lib/format"; // Next.js does not encode an utf-8 file name correctly when sending a form with a file attachment @@ -35,12 +34,7 @@ export const BillEditForm:FC = ({ location, bill }) => { const [ isPaid, setIsPaid ] = React.useState(paid); - // redirect to the main page - const handleCancel = () => { - gotoHome(location.yearMonth); - }; - - const billPaid_handleChange = (event: React.ChangeEvent) => { + const billPaid_handleChange = (event: React.ChangeEvent) => { setIsPaid(event.target.checked); } From 9a83d48e68c3d166a25b40ecc37fac148752f35c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Tue, 6 Feb 2024 15:42:10 +0100 Subject: [PATCH 02/12] removing legacy code --- app/ui/LocationEditForm.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/ui/LocationEditForm.tsx b/app/ui/LocationEditForm.tsx index 9b4aa27..97148bd 100644 --- a/app/ui/LocationEditForm.tsx +++ b/app/ui/LocationEditForm.tsx @@ -6,7 +6,6 @@ import { BillingLocation, YearMonth } from "../lib/db-types"; import { updateOrAddLocation } from "../lib/actions/locationActions"; import { useFormState } from "react-dom"; import Link from "next/link"; -import { gotoHome } from "../lib/actions/navigationActions"; export type LocationEditFormProps = { /** location which should be edited */ From 3579bb616ee280dd39da1196215ef90d448dac42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Thu, 8 Feb 2024 13:22:07 +0100 Subject: [PATCH 03/12] refactoring: renamed a component --- .../{MonthCardSceleton.tsx => MonthCardSkeleton.tsx} | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) rename app/ui/{MonthCardSceleton.tsx => MonthCardSkeleton.tsx} (77%) diff --git a/app/ui/MonthCardSceleton.tsx b/app/ui/MonthCardSkeleton.tsx similarity index 77% rename from app/ui/MonthCardSceleton.tsx rename to app/ui/MonthCardSkeleton.tsx index bc88b4b..d074358 100644 --- a/app/ui/MonthCardSceleton.tsx +++ b/app/ui/MonthCardSkeleton.tsx @@ -15,18 +15,9 @@ export interface MonthCardSkeletonProps { export const MonthCardSkeleton: React.FC = ({checked=false}) =>
-
-
+
; - - -export const HomePageSkeleton: React.FC = () => -<> - - - -; \ No newline at end of file From 4633b364749b99ff2b88f4f9d0d5d77c36cebc8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Thu, 8 Feb 2024 13:22:19 +0100 Subject: [PATCH 04/12] added API routers for locations --- app/api/all-locations/route.ts | 14 ++++++++++++++ app/api/available-years/route.ts | 9 +++++++++ 2 files changed, 23 insertions(+) create mode 100644 app/api/all-locations/route.ts create mode 100644 app/api/available-years/route.ts diff --git a/app/api/all-locations/route.ts b/app/api/all-locations/route.ts new file mode 100644 index 0000000..21179d9 --- /dev/null +++ b/app/api/all-locations/route.ts @@ -0,0 +1,14 @@ +import { fetchAllLocations } from '@/app/lib/actions/locationActions'; +import type { NextApiRequest } from 'next' +import { NextResponse } from 'next/server'; + +export const GET = async ( + req: NextApiRequest, +) => { + // get year from query params + const url = new URL(req.url as string); + const year = parseInt(url.searchParams.get('year') as string, 10); + const locations = await fetchAllLocations(year); + + return NextResponse.json({ locations }); +} diff --git a/app/api/available-years/route.ts b/app/api/available-years/route.ts new file mode 100644 index 0000000..310dfed --- /dev/null +++ b/app/api/available-years/route.ts @@ -0,0 +1,9 @@ +import { fetchAvailableYears } from '@/app/lib/actions/monthActions'; +import { NextResponse } from 'next/server'; + +export async function GET(request: Request) { + + const availableYears = await fetchAvailableYears(); + + return NextResponse.json({ availableYears }); +} \ No newline at end of file From 8703decb91140eade3b3a1dab06ab750d3b66a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Thu, 8 Feb 2024 13:23:23 +0100 Subject: [PATCH 05/12] HomePage: switched to client-side rendering --- app/lib/actions/monthActions.ts | 2 +- app/page.tsx | 12 +++-- app/ui/HomePage.tsx | 87 +++++++++++++++++++++++++-------- 3 files changed, 74 insertions(+), 27 deletions(-) diff --git a/app/lib/actions/monthActions.ts b/app/lib/actions/monthActions.ts index 7b3d843..03dcea8 100644 --- a/app/lib/actions/monthActions.ts +++ b/app/lib/actions/monthActions.ts @@ -70,7 +70,7 @@ export const fetchAvailableYears = withUser(async (user:AuthenticatedUser) => { const dbClient = await getDbClient(); // query mnogodb for all `yearMonth` values - const years = await dbClient.collection("lokacije") + const years:number[] = await dbClient.collection("lokacije") .distinct("yearMonth.year", { userId }) // sort the years in descending order diff --git a/app/page.tsx b/app/page.tsx index d321cd7..01601df 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,7 +1,11 @@ import { FC, Suspense } from 'react'; import { Main } from './ui/Main'; -import HomePage from './ui/HomePage'; -import { HomePageSkeleton } from './ui/MonthCardSceleton'; +import dynamic from 'next/dynamic' + +const HomePage = dynamic( + () => import('./ui/HomePage'), + { ssr: false } +) export interface PageProps { searchParams?: { @@ -14,9 +18,7 @@ const Page:FC = async ({ searchParams }) => { return (
- }> - - +
); } diff --git a/app/ui/HomePage.tsx b/app/ui/HomePage.tsx index 2f3f59a..821366e 100644 --- a/app/ui/HomePage.tsx +++ b/app/ui/HomePage.tsx @@ -1,30 +1,79 @@ -import { fetchAllLocations } from '@/app/lib/actions/locationActions'; -import { fetchAvailableYears } from '@/app/lib/actions/monthActions'; +"use client"; + import { BillingLocation, YearMonth } from '@/app/lib/db-types'; -import { FC } from 'react'; +import { FC, useEffect, useState } from 'react'; import { MonthLocationList } from '@/app/ui/MonthLocationList'; +import { WithId } from 'mongodb'; +import { MonthCardSkeleton } from './MonthCardSkeleton'; export interface HomePageProps { - searchParams?: { - year?: string; - month?: string; - }; } -export const HomePage:FC = async ({ searchParams }) => { +const fetchAllLocations = async (year: number) => { + const response = await fetch(`/api/all-locations/?year=${year}`); + const { locations } : { locations: WithId[] } = await response.json(); + return locations; +} - let availableYears: number[]; +const fetchAvailableYears = async () => { + const response = await fetch(`/api/available-years/`); + const { availableYears }: { availableYears: number[]} = await response.json(); + return availableYears; +} - // const asyncTimout = (ms:number) => new Promise(resolve => setTimeout(resolve, ms)); - // await asyncTimout(5000); +export const HomePage:FC = () => { - try { - availableYears = await fetchAvailableYears(); - } catch (error:any) { + const [ homePageStatus, setHomePageStatus ] = useState<{ + status: "loading" | "loaded" | "error", + availableYears: number[], + locations: WithId[], + error?: string + }>({ + status: "loading", + availableYears: [], + locations: [] + }); + + const {availableYears, locations, status, error} = homePageStatus; + + const year = new URLSearchParams(window.location.search).get('year'); + const currentYear = year ? parseInt(year, 10) : new Date().getFullYear(); + + useEffect(() => { + + const fetchData = async () => { + + try { + setHomePageStatus({ + availableYears: await fetchAvailableYears(), + locations: await fetchAllLocations(currentYear), + status: "loaded", + }); + } catch (error: any) { + setHomePageStatus({ + status: "error", + availableYears: [], + locations: [], + error: error.message + }); + } + } + + fetchData(); + }, [currentYear]); + + if(status === "loading") { return ( -
-

{error.message}

-
); + <> + + + + + ); + } + + if(status === "error") { + return(

{error}

); } // if the database is in it's initial state, show the add location button for the current month @@ -32,10 +81,6 @@ export const HomePage:FC = async ({ searchParams }) => { return (); } - const currentYear = Number(searchParams?.year) || availableYears[0]; - - const locations = await fetchAllLocations(currentYear); - // group locations by month const months = locations.reduce((acc, location) => { const {year, month} = location.yearMonth; From 3bd487d126e7cc6dbdb59a4b08971949c2426ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Thu, 8 Feb 2024 13:27:28 +0100 Subject: [PATCH 06/12] refactor: renamed API dirs --- app/api/{ => locations}/available-years/route.ts | 0 app/api/{all-locations => locations/in-year}/route.ts | 0 app/ui/HomePage.tsx | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename app/api/{ => locations}/available-years/route.ts (100%) rename app/api/{all-locations => locations/in-year}/route.ts (100%) diff --git a/app/api/available-years/route.ts b/app/api/locations/available-years/route.ts similarity index 100% rename from app/api/available-years/route.ts rename to app/api/locations/available-years/route.ts diff --git a/app/api/all-locations/route.ts b/app/api/locations/in-year/route.ts similarity index 100% rename from app/api/all-locations/route.ts rename to app/api/locations/in-year/route.ts diff --git a/app/ui/HomePage.tsx b/app/ui/HomePage.tsx index 821366e..091af1b 100644 --- a/app/ui/HomePage.tsx +++ b/app/ui/HomePage.tsx @@ -10,13 +10,13 @@ export interface HomePageProps { } const fetchAllLocations = async (year: number) => { - const response = await fetch(`/api/all-locations/?year=${year}`); + const response = await fetch(`/api/locations/in-year/?year=${year}`); const { locations } : { locations: WithId[] } = await response.json(); return locations; } const fetchAvailableYears = async () => { - const response = await fetch(`/api/available-years/`); + const response = await fetch(`/api/locations/available-years/`); const { availableYears }: { availableYears: number[]} = await response.json(); return availableYears; } From 0bb4c1206197650c6e73002adae9e74a7ccac448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Thu, 8 Feb 2024 13:50:48 +0100 Subject: [PATCH 07/12] LocationEditForm: migrated to client-side rendering --- app/api/locations/by-id/route.ts | 13 +++++ app/location/[id]/edit/LocationEditPage.tsx | 57 +++++++++++++++++---- app/location/[id]/edit/page.tsx | 6 +-- app/ui/LocationEditForm.tsx | 17 +++--- 4 files changed, 71 insertions(+), 22 deletions(-) create mode 100644 app/api/locations/by-id/route.ts diff --git a/app/api/locations/by-id/route.ts b/app/api/locations/by-id/route.ts new file mode 100644 index 0000000..605b5d6 --- /dev/null +++ b/app/api/locations/by-id/route.ts @@ -0,0 +1,13 @@ +import { fetchLocationById } from '@/app/lib/actions/locationActions'; +import type { NextApiRequest } from 'next' +import { NextResponse } from 'next/server'; + +export const GET = async ( + req: NextApiRequest, +) => { + const url = new URL(req.url as string); + const locationId = url.searchParams.get('id'); + const location = await fetchLocationById(locationId as string); + + return NextResponse.json({ location }); +} diff --git a/app/location/[id]/edit/LocationEditPage.tsx b/app/location/[id]/edit/LocationEditPage.tsx index 505f634..df97fad 100644 --- a/app/location/[id]/edit/LocationEditPage.tsx +++ b/app/location/[id]/edit/LocationEditPage.tsx @@ -1,16 +1,53 @@ +"use client"; + import { notFound } from 'next/navigation'; -import { LocationEditForm } from '@/app/ui/LocationEditForm'; -import { fetchLocationById } from '@/app/lib/actions/locationActions'; +import { LocationEditForm, LocationEditFormSkeleton } from '@/app/ui/LocationEditForm'; +import { useEffect, useState } from 'react'; +import { WithId } from 'mongodb'; +import { BillingLocation } from '@/app/lib/db-types'; -export default async function LocationEditPage({ locationId }: { locationId:string }) { - const location = await fetchLocationById(locationId); +const fetchLocationById = async (locationId: string) => { + const response = await fetch(`/api/locations/by-id?id=${locationId}`); + const json = await response.json(); + return json.location as WithId; +} - if (!location) { - return(notFound()); +export default function LocationEditPage({ locationId }: { locationId:string }) { + + const [state, stateSet] = useState<{ + status: 'loading' | 'error' | 'success'; + location?: WithId; + error?: string; + }>({ status: 'loading' }); + + useEffect(() => { + + const fetchLocation = async () => { + try { + const location = await fetchLocationById(locationId); + stateSet({ location, status: 'success' }); + } catch(error:any) { + stateSet({ status: 'error', error: error.message }); + } + }; + + fetchLocation(); + + }, [locationId]); + + switch(state.status) { + case "error": + return(
Error: {state.error}
); + case "loading": + return(); + case "success": + if (!state.location) { + return(notFound()); + } + + return(); + default: + return(
Error: Unknown status
); } - - const result = ; - - return (result); } \ No newline at end of file diff --git a/app/location/[id]/edit/page.tsx b/app/location/[id]/edit/page.tsx index 251f944..7e63bcb 100644 --- a/app/location/[id]/edit/page.tsx +++ b/app/location/[id]/edit/page.tsx @@ -1,15 +1,11 @@ -import { Suspense } from 'react'; import LocationEditPage from './LocationEditPage'; import { Main } from '@/app/ui/Main'; -import { LocationEditFormSkeleton } from '@/app/ui/LocationEditForm'; export default async function Page({ params:{ id } }: { params: { id:string } }) { return (
- }> - - +
); } \ No newline at end of file diff --git a/app/ui/LocationEditForm.tsx b/app/ui/LocationEditForm.tsx index 97148bd..1c1f86a 100644 --- a/app/ui/LocationEditForm.tsx +++ b/app/ui/LocationEditForm.tsx @@ -65,10 +65,9 @@ export const LocationEditForm:FC = ({ location, yearMonth

} -
- - Cancel + + Cancel
@@ -79,10 +78,14 @@ export const LocationEditForm:FC = ({ location, yearMonth export const LocationEditFormSkeleton:FC = () => { return( -
-
- - +
+
+
+
+
+
+
+
) From e2f7b21a5e838e55ee3440b9bf144cd85136afb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Thu, 8 Feb 2024 14:04:48 +0100 Subject: [PATCH 08/12] BugFix: HomePage did not parse URL correctly --- app/ui/HomePage.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/ui/HomePage.tsx b/app/ui/HomePage.tsx index 091af1b..095d0dd 100644 --- a/app/ui/HomePage.tsx +++ b/app/ui/HomePage.tsx @@ -5,6 +5,7 @@ import { FC, useEffect, useState } from 'react'; import { MonthLocationList } from '@/app/ui/MonthLocationList'; import { WithId } from 'mongodb'; import { MonthCardSkeleton } from './MonthCardSkeleton'; +import { useSearchParams } from 'next/navigation'; export interface HomePageProps { } @@ -23,6 +24,8 @@ const fetchAvailableYears = async () => { export const HomePage:FC = () => { + const searchParams = useSearchParams(); + const [ homePageStatus, setHomePageStatus ] = useState<{ status: "loading" | "loaded" | "error", availableYears: number[], @@ -36,7 +39,7 @@ export const HomePage:FC = () => { const {availableYears, locations, status, error} = homePageStatus; - const year = new URLSearchParams(window.location.search).get('year'); + const year = searchParams.get('year'); const currentYear = year ? parseInt(year, 10) : new Date().getFullYear(); useEffect(() => { From ee02cc4f32b5a4da7fe035bc4f2871acbc1311f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Thu, 8 Feb 2024 14:07:09 +0100 Subject: [PATCH 09/12] LocationAddPage migrated to client-side component --- app/location/[id]/add/LocationAddPage.tsx | 5 ++--- app/location/[id]/add/page.tsx | 6 +----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/app/location/[id]/add/LocationAddPage.tsx b/app/location/[id]/add/LocationAddPage.tsx index ebef49f..a099506 100644 --- a/app/location/[id]/add/LocationAddPage.tsx +++ b/app/location/[id]/add/LocationAddPage.tsx @@ -1,8 +1,7 @@ -import { notFound } from 'next/navigation'; +"use client"; + import { LocationEditForm } from '@/app/ui/LocationEditForm'; -import { fetchLocationById } from '@/app/lib/actions/locationActions'; import { YearMonth } from '@/app/lib/db-types'; -import { Main } from '@/app/ui/Main'; export default async function LocationAddPage({ yearMonth }: { yearMonth:YearMonth }) { return (); diff --git a/app/location/[id]/add/page.tsx b/app/location/[id]/add/page.tsx index fed90e0..3f032fc 100644 --- a/app/location/[id]/add/page.tsx +++ b/app/location/[id]/add/page.tsx @@ -1,15 +1,11 @@ import { parseYearMonth } from '@/app/lib/format'; -import { LocationEditFormSkeleton } from '@/app/ui/LocationEditForm'; import LocationAddPage from './LocationAddPage'; import { Main } from '@/app/ui/Main'; -import { Suspense } from 'react'; export default async function Page({ params:{ id } }: { params: { id:string } }) { return (
- }> - - +
); } \ No newline at end of file From a96998baad5f4f32ee43a2e318a50f53c6c67e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Thu, 8 Feb 2024 14:23:01 +0100 Subject: [PATCH 10/12] Location Edit / Add / Delete migrated to client-side rendering --- app/location/[id]/add/page.tsx | 7 ++- .../[id]/delete/LocationDeletePage.tsx | 56 ++++++++++++++++--- app/location/[id]/delete/page.tsx | 16 +++--- app/location/[id]/edit/page.tsx | 8 ++- app/ui/LocationDeleteForm.tsx | 16 +++++- app/ui/LocationEditForm.tsx | 12 ++-- 6 files changed, 89 insertions(+), 26 deletions(-) diff --git a/app/location/[id]/add/page.tsx b/app/location/[id]/add/page.tsx index 3f032fc..87d0ddf 100644 --- a/app/location/[id]/add/page.tsx +++ b/app/location/[id]/add/page.tsx @@ -1,6 +1,11 @@ import { parseYearMonth } from '@/app/lib/format'; -import LocationAddPage from './LocationAddPage'; import { Main } from '@/app/ui/Main'; +import dynamic from 'next/dynamic' + +const LocationAddPage = dynamic( + () => import('./LocationAddPage'), + { ssr: false } + ) export default async function Page({ params:{ id } }: { params: { id:string } }) { return ( diff --git a/app/location/[id]/delete/LocationDeletePage.tsx b/app/location/[id]/delete/LocationDeletePage.tsx index 7377733..ec8663c 100644 --- a/app/location/[id]/delete/LocationDeletePage.tsx +++ b/app/location/[id]/delete/LocationDeletePage.tsx @@ -1,14 +1,54 @@ +"use client"; + import { notFound } from 'next/navigation'; -import { fetchLocationById } from '@/app/lib/actions/locationActions'; -import { LocationDeleteForm } from '@/app/ui/LocationDeleteForm'; +import { LocationDeleteForm, LocationDeleteFormSkeleton } from '@/app/ui/LocationDeleteForm'; +import { WithId } from 'mongodb'; +import { BillingLocation } from '@/app/lib/db-types'; +import { useEffect, useState } from 'react'; -export const LocationDeletePage = async ({ locationId }: { locationId:string }) => { +const fetchLocationById = async (locationId: string) => { + const response = await fetch(`/api/locations/by-id?id=${locationId}`); + const json = await response.json(); + return json.location as WithId; +} - const location = await fetchLocationById(locationId); +const LocationDeletePage = ({ locationId }: { locationId:string }) => { + + const [state, stateSet] = useState<{ + status: 'loading' | 'error' | 'success'; + location?: WithId; + error?: string; + }>({ status: 'loading' }); - if (!location) { - return(notFound()); + useEffect(() => { + + const fetchLocation = async () => { + try { + const location = await fetchLocationById(locationId); + stateSet({ location, status: 'success' }); + } catch(error:any) { + stateSet({ status: 'error', error: error.message }); + } + }; + + fetchLocation(); + + }, [locationId]); + + switch(state.status) { + case "error": + return(
Error: {state.error}
); + case "loading": + return(); + case "success": + if (!state.location) { + return(notFound()); + } + + return(); + default: + return(
Error: Unknown status
); } +} - return (); -} \ No newline at end of file +export default LocationDeletePage; \ No newline at end of file diff --git a/app/location/[id]/delete/page.tsx b/app/location/[id]/delete/page.tsx index 6ae17d5..ca0db96 100644 --- a/app/location/[id]/delete/page.tsx +++ b/app/location/[id]/delete/page.tsx @@ -1,17 +1,17 @@ -import { notFound } from 'next/navigation'; -import { fetchLocationById } from '@/app/lib/actions/locationActions'; -import { LocationDeleteForm } from '@/app/ui/LocationDeleteForm'; import { Main } from '@/app/ui/Main'; -import { Suspense } from 'react'; -import { LocationDeletePage } from './LocationDeletePage'; +import dynamic from 'next/dynamic' + +const LocationDeletePage = dynamic( + () => import('./LocationDeletePage'), + { ssr: false } + ) + export default async function Page({ params:{ id } }: { params: { id:string } }) { return (
- Loading...
}> - - + ); } \ No newline at end of file diff --git a/app/location/[id]/edit/page.tsx b/app/location/[id]/edit/page.tsx index 7e63bcb..0fb2160 100644 --- a/app/location/[id]/edit/page.tsx +++ b/app/location/[id]/edit/page.tsx @@ -1,5 +1,11 @@ -import LocationEditPage from './LocationEditPage'; import { Main } from '@/app/ui/Main'; +import dynamic from 'next/dynamic' + +const LocationEditPage = dynamic( + () => import('./LocationEditPage'), + { ssr: false } + ) + export default async function Page({ params:{ id } }: { params: { id:string } }) { diff --git a/app/ui/LocationDeleteForm.tsx b/app/ui/LocationDeleteForm.tsx index 35eb104..7f6651d 100644 --- a/app/ui/LocationDeleteForm.tsx +++ b/app/ui/LocationDeleteForm.tsx @@ -29,11 +29,23 @@ export const LocationDeleteForm:FC = ({ location }) => Please confirm deletion of location “{location.name}”.

- - Cancel + + Cancel
); } + + +export const LocationDeleteFormSkeleton:FC = () => +
+
+

+
+
+
+
+
+
\ No newline at end of file diff --git a/app/ui/LocationEditForm.tsx b/app/ui/LocationEditForm.tsx index 1c1f86a..b1fdc29 100644 --- a/app/ui/LocationEditForm.tsx +++ b/app/ui/LocationEditForm.tsx @@ -47,7 +47,7 @@ export const LocationEditForm:FC = ({ location, yearMonth ))} - +
{state.errors?.locationNotes && state.errors.locationNotes.map((error: string) => ( @@ -79,12 +79,12 @@ export const LocationEditFormSkeleton:FC = () => { return(
-
-
-
+
+
+
-
-
+
+
From 14c043850049aaf9317a60166baea7fe7ddbbf1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Thu, 8 Feb 2024 14:43:38 +0100 Subject: [PATCH 11/12] HomePage: state optimization --- app/ui/HomePage.tsx | 84 ++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/app/ui/HomePage.tsx b/app/ui/HomePage.tsx index 095d0dd..262da6c 100644 --- a/app/ui/HomePage.tsx +++ b/app/ui/HomePage.tsx @@ -10,6 +10,14 @@ import { useSearchParams } from 'next/navigation'; export interface HomePageProps { } +type MonthsLocations = { + [key:string]:{ + yearMonth: YearMonth, + locations: BillingLocation[], + monthlyExpense: number + } +} + const fetchAllLocations = async (year: number) => { const response = await fetch(`/api/locations/in-year/?year=${year}`); const { locations } : { locations: WithId[] } = await response.json(); @@ -25,38 +33,66 @@ const fetchAvailableYears = async () => { export const HomePage:FC = () => { const searchParams = useSearchParams(); + const year = searchParams.get('year'); + const currentYear = year ? parseInt(year, 10) : new Date().getFullYear(); const [ homePageStatus, setHomePageStatus ] = useState<{ status: "loading" | "loaded" | "error", availableYears: number[], - locations: WithId[], + months?: MonthsLocations, error?: string }>({ status: "loading", availableYears: [], - locations: [] }); - const {availableYears, locations, status, error} = homePageStatus; - - const year = searchParams.get('year'); - const currentYear = year ? parseInt(year, 10) : new Date().getFullYear(); + const {availableYears, months, status, error} = homePageStatus; useEffect(() => { const fetchData = async () => { try { + const locations = await fetchAllLocations(currentYear); + + // group locations by month + const months = locations.reduce((acc, location) => { + const {year, month} = location.yearMonth; + const key = `${year}-${month}`; + + const locationsInMonth = acc[key]; + + if(locationsInMonth) { + return({ + ...acc, + [key]: { + yearMonth: location.yearMonth, + locations: [...locationsInMonth.locations, location], + monthlyExpense: locationsInMonth.monthlyExpense + location.bills.reduce((acc, bill) => bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0) + } + }) + } + + return({ + ...acc, + [key]: { + yearMonth: location.yearMonth, + locations: [location], + monthlyExpense: location.bills.reduce((acc, bill) => bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0) + } + }); + }, {} as MonthsLocations); + setHomePageStatus({ availableYears: await fetchAvailableYears(), - locations: await fetchAllLocations(currentYear), + months, status: "loaded", }); + } catch (error: any) { setHomePageStatus({ status: "error", availableYears: [], - locations: [], error: error.message }); } @@ -84,38 +120,6 @@ export const HomePage:FC = () => { return (); } - // group locations by month - const months = locations.reduce((acc, location) => { - const {year, month} = location.yearMonth; - const key = `${year}-${month}`; - - const locationsInMonth = acc[key]; - - if(locationsInMonth) { - return({ - ...acc, - [key]: { - yearMonth: location.yearMonth, - locations: [...locationsInMonth.locations, location], - monthlyExpense: locationsInMonth.monthlyExpense + location.bills.reduce((acc, bill) => bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0) - } - }) - } - - return({ - ...acc, - [key]: { - yearMonth: location.yearMonth, - locations: [location], - monthlyExpense: location.bills.reduce((acc, bill) => bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0) - } - }); - }, {} as {[key:string]:{ - yearMonth: YearMonth, - locations: BillingLocation[], - monthlyExpense: number - } }); - return ( ); From ab61657c03a3e74367e1ae66edd91e4405bcc02e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Thu, 8 Feb 2024 14:44:56 +0100 Subject: [PATCH 12/12] updated version in docker-compose --- docker-compose-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose-deploy.yml b/docker-compose-deploy.yml index a39518f..3dae28e 100644 --- a/docker-compose-deploy.yml +++ b/docker-compose-deploy.yml @@ -9,7 +9,7 @@ networks: services: web-app: - image: utility-bills-tracker:1.8.0 + image: utility-bills-tracker:1.9.0 networks: - traefik-network - mongo-network