Files
evidencija-rezija/web-app/app/ui/HomePage.tsx
Nikola Derežić 37f617683e (refactor + bugfix) Improve data structure and handle empty database edge cases
Refactored months data structure from object to array for better performance
and cleaner iteration. Fixed crash when availableYears array is empty by
adding proper guards and fallback to current year.

Changes:
- MonthLocationList: Changed months prop from object to array type
- HomePage: Refactored reduce logic to build array instead of object
- HomePage: Added empty database handling in year selection logic
- HomePage: Added early returns for invalid year params in empty DB

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 17:36:10 +01:00

107 lines
3.4 KiB
TypeScript

import { fetchAllLocations } from '@/app/lib/actions/locationActions';
import { fetchAvailableYears } from '@/app/lib/actions/monthActions';
import { getUserSettings } from '@/app/lib/actions/userSettingsActions';
import { FC } from 'react';
import { MonthArray, MonthLocationList } from '@/app/ui/MonthLocationList';
import { ParamsYearInvalidMessage } from './ParamsYearInvalidMessage';
export interface HomePageProps {
searchParams?: {
year?: string;
month?: string;
};
}
export const HomePage:FC<HomePageProps> = async ({ searchParams }) => {
/** years found in the DB sorted descending */
let availableYears: number[];
try {
availableYears = await fetchAvailableYears();
} catch (error:any) {
return (
<main className="flex min-h-screen flex-col p-6 bg-base-300">
<p className="text-center text-2xl text-red-500">{error.message}</p>
</main>);
}
const paramsYear = searchParams?.year ? Number(searchParams.year) : null;
let selectedYear:number;
// IF year is set via params, check if it's valid
if(paramsYear) {
// IF the database is in it's initial state
// THEN show message param being invalid AND redirect to root page
if(availableYears.length === 0) {
return (<ParamsYearInvalidMessage />);
}
// IF the year specified in the params is not found in the DB
// THEN show message param being invalid AND redirect to page showing first available year
if(!availableYears.includes(paramsYear)) {
return (<ParamsYearInvalidMessage firstAvailableYear={availableYears[0]} />);
}
selectedYear = paramsYear;
} else {
const currYear = new Date().getFullYear();
if(availableYears.length === 0) {
// Database is in it's initial state
// so just set selected year to current year
selectedYear = currYear;
} else {
// IF current year is available in DB THEN use it
// ELSE use the latest year found in the DB
selectedYear = availableYears.includes(currYear) ? currYear : availableYears[0];
}
}
const locations = await fetchAllLocations(selectedYear);
const userSettings = await getUserSettings();
// Create months object by grouping locations by yearMonth
const { months } = locations.reduce((acc, location) => {
const {year, month} = location.yearMonth;
const key = `${year}-${month}`;
const monthIx = acc.index[key];
if(monthIx) {
const existingMonth = acc.months[monthIx];
existingMonth.locations.push(location);
existingMonth.unpaidTotal += location.bills.reduce((acc, bill) => !bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0);
existingMonth.payedTotal += location.bills.reduce((acc, bill) => bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0);
return acc;
}
acc.months.push({
yearMonth: location.yearMonth,
locations: [location],
unpaidTotal: location.bills.reduce((acc, bill) => !bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0),
payedTotal: location.bills.reduce((acc, bill) => bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0)
});
acc.index[key] = acc.months.length - 1;
return acc;
}, {
index: {},
months: []
} as {
index: Record<string, number>,
months: MonthArray
});
return (
<MonthLocationList availableYears={availableYears} months={months} userSettings={userSettings} />
);
}
export default HomePage;