Add initial project setup with PocketBase integration, global state management, and UI components for gift tracking

This commit is contained in:
Norbert Maciaszek
2025-11-11 15:25:29 +01:00
parent 1fd7b56754
commit 60d0888a49
30 changed files with 1140 additions and 111 deletions

View File

@@ -0,0 +1,31 @@
'use client';
import { FC, useState } from 'react';
import { ActionCard } from '../../molecules/ActionCard';
import { PersonModal } from '../../molecules/PersonModal';
import { DB } from '@/lib/db';
import { useRouter } from 'next/navigation';
type Props = {
year: string;
};
export const YearControls: FC<Props> = ({ year }) => {
const [modalOpen, setModalOpen] = useState<'addPerson' | null>(null);
const router = useRouter();
return (
<>
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 my-8'>
<ActionCard title='Dodaj nową osobę' onClick={() => setModalOpen('addPerson')} backgroundColor='bg-green-50' />
</div>
<PersonModal
isOpen={modalOpen === 'addPerson'}
year={year}
onClose={() => setModalOpen(null)}
onSave={async (data) => {
await DB.createPerson(data);
router.refresh();
}}
/>
</>
);
};

View File

@@ -0,0 +1,51 @@
import { StatsCard } from '@/components/atoms/StatsCard';
import { DB } from '@/lib/db';
import { formatCurrency } from '@/helpers/formatCurrency';
import { FC } from 'react';
import { Heading } from '@/components/atoms/Heading';
type Props = {
year?: number;
};
export const YearOverview: FC<Props> = async ({ year }) => {
const currentYear = new Date().getFullYear();
const data = await DB.getYear(year ?? currentYear);
if (!data) {
return null;
}
const gifts = await Promise.all(
data.gifts.map(async (gift) => {
return await DB.getGift(gift);
}),
);
const totalCost = gifts.reduce((acc, gift) => acc + gift.cost, 0);
const averageCost = totalCost / gifts.length;
return (
<div className='mb-6 md:mb-8'>
<Heading size='large'>Podsumowanie roku {data.year}</Heading>
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4'>
<StatsCard title='Planowane prezenty' value={gifts.length} />
<StatsCard
title='Łączny koszt'
value={formatCurrency(totalCost)}
description={`Średnio: ${formatCurrency(averageCost)}`}
/>
<StatsCard
title='Status: Do kupienia'
value={gifts.filter((gift) => gift.status === 'decided').length}
description='Prezenty do kupienia'
/>
<StatsCard
title='Status: Kupione'
value={gifts.filter((gift) => gift.status === 'bought').length}
description='Prezenty które zostały już kupione'
/>
</div>
</div>
);
};