Refactor Home component to display gifts for the current year using YearGifts, update YearOverview to accept a custom heading, and enhance YearList button text for clarity. Introduce new gift status 'ready' across relevant components and adjust GiftCard styling for better status representation.
This commit is contained in:
@@ -1,20 +1,21 @@
|
|||||||
import { YearOverview } from '@/components/organisms/YearOverview';
|
import { YearOverview } from '@/components/organisms/YearOverview';
|
||||||
import { YearList } from '@/components/atoms/YearList';
|
import { YearList } from '@/components/atoms/YearList';
|
||||||
import { DB } from '@/lib/db';
|
import { DB } from '@/lib/db';
|
||||||
import { GiftCard } from '@/components/molecules/GiftCard';
|
|
||||||
import { Heading } from '@/components/atoms/Heading';
|
import { Heading } from '@/components/atoms/Heading';
|
||||||
|
import { YearGifts } from '@/components/organisms/YearGifts';
|
||||||
|
|
||||||
|
export const dynamic = 'force-dynamic';
|
||||||
|
|
||||||
export default async function Home() {
|
export default async function Home() {
|
||||||
const recentGifts = await DB.getRecentGifts();
|
const year = new Date().getFullYear();
|
||||||
|
const gifts = await DB.getGifts(year);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<YearOverview />
|
<YearOverview heading='Podsumowanie bieżącego roku' />
|
||||||
<YearList />
|
<YearList />
|
||||||
<Heading>Ostatnie prezenty</Heading>
|
<Heading>Prezenty tego roku</Heading>
|
||||||
{recentGifts.map((gift) => (
|
<YearGifts gifts={gifts} />
|
||||||
<GiftCard key={gift.id} {...gift} />
|
|
||||||
))}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export const YearList = async () => {
|
|||||||
<div className='mb-6 flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between'>
|
<div className='mb-6 flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between'>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<Button variant='secondary' href='/'>
|
<Button variant='secondary' href='/'>
|
||||||
Wszystkie lata
|
Prezenty tego roku
|
||||||
</Button>
|
</Button>
|
||||||
{years.map((year) => (
|
{years.map((year) => (
|
||||||
<Button key={year.id} href={`/year/${year.year}`} variant='secondary'>
|
<Button key={year.id} href={`/year/${year.year}`} variant='secondary'>
|
||||||
|
|||||||
@@ -26,9 +26,10 @@ export const GiftCard: FC<Gift & Props> = ({ hideDetails = false, editable = fal
|
|||||||
|
|
||||||
const bgByStatus = {
|
const bgByStatus = {
|
||||||
planned: 'bg-gray-100',
|
planned: 'bg-gray-100',
|
||||||
decided: 'bg-blue-100',
|
decided: 'bg-red-300',
|
||||||
bought: 'bg-green-100',
|
bought: 'bg-yellow-200',
|
||||||
wrapped: 'bg-purple-100',
|
ready: 'bg-green-100',
|
||||||
|
wrapped: 'bg-green-300',
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async () => {
|
const handleDelete = async () => {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ type Props = {
|
|||||||
link: string;
|
link: string;
|
||||||
cost: number;
|
cost: number;
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
status: 'planned' | 'decided' | 'bought' | 'wrapped';
|
status: Gift['status'];
|
||||||
year: string;
|
year: string;
|
||||||
person: string;
|
person: string;
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
@@ -148,6 +148,7 @@ export const GiftModal: FC<Props> = ({ isOpen, onClose, gift, yearId, personId,
|
|||||||
<option value='planned'>{formatStatus('planned')} </option>
|
<option value='planned'>{formatStatus('planned')} </option>
|
||||||
<option value='decided'>{formatStatus('decided')}</option>
|
<option value='decided'>{formatStatus('decided')}</option>
|
||||||
<option value='bought'>{formatStatus('bought')}</option>
|
<option value='bought'>{formatStatus('bought')}</option>
|
||||||
|
<option value='ready'>{formatStatus('ready')}</option>
|
||||||
<option value='wrapped'>{formatStatus('wrapped')}</option>
|
<option value='wrapped'>{formatStatus('wrapped')}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const PersonCardEdit: FC<Props> = ({ person }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4'>
|
<div className='flex flex-row items-center justify-center gap-2'>
|
||||||
<Button variant='primary' onClick={() => setIsOpen(true)}>
|
<Button variant='primary' onClick={() => setIsOpen(true)}>
|
||||||
Edytuj
|
Edytuj
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const PersonCard: FC<Person> = (person) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='bg-white rounded-2xl shadow-sm hover:shadow-md p-4 md:p-6 transition-all duration-200 ease-in-out mt-6'>
|
<div className='bg-white rounded-2xl shadow-sm hover:shadow-md p-4 md:p-6 transition-all duration-200 ease-in-out mt-6'>
|
||||||
<div className='flex flex-col sm:items-center sm:justify-between gap-4 pb-4 mb-6 border-b border-gray-200'>
|
<div className='flex flex-col items-center sm:justify-between gap-4 pb-4 mb-6 border-b border-gray-200'>
|
||||||
<Heading size='large' spacing='none'>
|
<Heading size='large' spacing='none'>
|
||||||
{name}
|
{name}
|
||||||
</Heading>
|
</Heading>
|
||||||
|
|||||||
56
src/components/organisms/YearGifts/index.tsx
Normal file
56
src/components/organisms/YearGifts/index.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
'use client';
|
||||||
|
import { GiftCard } from '@/components/molecules/GiftCard';
|
||||||
|
import { FC, useState } from 'react';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
gifts: Gift[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const YearGifts: FC<Props> = ({ gifts }) => {
|
||||||
|
const [filter, setFilter] = useState<Gift['status'] | 'all'>('all');
|
||||||
|
const [sort, setSort] = useState<keyof Pick<Gift, 'title' | 'cost' | 'person' | 'status'>>('title');
|
||||||
|
let renderGifts = gifts;
|
||||||
|
|
||||||
|
// sort
|
||||||
|
if (sort === 'cost') {
|
||||||
|
renderGifts = renderGifts.sort((a, b) => b.cost - a.cost);
|
||||||
|
} else if (sort === 'person') {
|
||||||
|
renderGifts = renderGifts.sort((a, b) => a.expand.person.name.localeCompare(b.expand.person.name));
|
||||||
|
} else {
|
||||||
|
renderGifts = renderGifts.sort((a, b) => (a[sort] < b[sort] ? -1 : 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter !== 'all') {
|
||||||
|
renderGifts = renderGifts.filter((gift) => gift.status === filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className=''>
|
||||||
|
<div className='flex flex-row items-center justify-between gap-4'>
|
||||||
|
<select
|
||||||
|
className='px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all'
|
||||||
|
value={filter}
|
||||||
|
onChange={(e) => setFilter(e.target.value as Gift['status'] | 'all')}>
|
||||||
|
<option value='all'>Wszystkie</option>
|
||||||
|
<option value='planned'>Planowane</option>
|
||||||
|
<option value='decided'>Do kupienia</option>
|
||||||
|
<option value='bought'>Kupione</option>
|
||||||
|
<option value='wrapped'>Gotowe</option>
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
className='px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all'
|
||||||
|
value={sort}
|
||||||
|
onChange={(e) => setSort(e.target.value as keyof Pick<Gift, 'title' | 'cost' | 'person' | 'status'>)}>
|
||||||
|
<option value='title'>Tytuł</option>
|
||||||
|
<option value='cost'>Koszt</option>
|
||||||
|
<option value='person'>Osoba</option>
|
||||||
|
<option value='status'>Status</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{renderGifts.map((gift) => (
|
||||||
|
<GiftCard key={gift.id} {...gift} />
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -5,10 +5,11 @@ import { FC } from 'react';
|
|||||||
import { Heading } from '@/components/atoms/Heading';
|
import { Heading } from '@/components/atoms/Heading';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
heading?: string;
|
||||||
year?: number;
|
year?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const YearOverview: FC<Props> = async ({ year }) => {
|
export const YearOverview: FC<Props> = async ({ year, heading }) => {
|
||||||
const currentYear = new Date().getFullYear();
|
const currentYear = new Date().getFullYear();
|
||||||
const data = await DB.getYear(year ?? currentYear);
|
const data = await DB.getYear(year ?? currentYear);
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ export const YearOverview: FC<Props> = async ({ year }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mb-6 md:mb-8'>
|
<div className='mb-6 md:mb-8'>
|
||||||
<Heading size='large'>Podsumowanie roku {data.year}</Heading>
|
<Heading size='large'>{heading ?? `Podsumowanie roku ${data.year}`}</Heading>
|
||||||
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4'>
|
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4'>
|
||||||
<StatsCard title='Ilość prezentów' value={gifts.length} />
|
<StatsCard title='Ilość prezentów' value={gifts.length} />
|
||||||
<StatsCard title='Planowane prezenty' value={planned.length} />
|
<StatsCard title='Planowane prezenty' value={planned.length} />
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ export const formatStatus = (status: Gift['status']) => {
|
|||||||
return 'Do kupienia';
|
return 'Do kupienia';
|
||||||
case 'bought':
|
case 'bought':
|
||||||
return 'Kupione';
|
return 'Kupione';
|
||||||
|
case 'ready':
|
||||||
|
return 'Do spakowania';
|
||||||
case 'wrapped':
|
case 'wrapped':
|
||||||
return 'Gotowe';
|
return 'Gotowe';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export const DB = {
|
|||||||
link: string;
|
link: string;
|
||||||
cost: number;
|
cost: number;
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
status: 'planned' | 'decided' | 'bought' | 'wrapped';
|
status: Gift['status'];
|
||||||
year: string;
|
year: string;
|
||||||
person: string;
|
person: string;
|
||||||
}): Promise<Gift> => {
|
}): Promise<Gift> => {
|
||||||
@@ -71,7 +71,7 @@ export const DB = {
|
|||||||
link: string;
|
link: string;
|
||||||
cost: number;
|
cost: number;
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
status: 'planned' | 'decided' | 'bought' | 'wrapped';
|
status: Gift['status'];
|
||||||
year: string;
|
year: string;
|
||||||
person: string;
|
person: string;
|
||||||
},
|
},
|
||||||
@@ -88,9 +88,14 @@ export const DB = {
|
|||||||
'gifts-': id,
|
'gifts-': id,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getGifts: async (yearId: string): Promise<Gift[]> => {
|
getGifts: async (year: number): Promise<Gift[]> => {
|
||||||
|
const yearRecord = await pb.collection('gifts_year').getFirstListItem(`year = ${year}`);
|
||||||
|
if (!yearRecord) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return await pb.collection('gifts_items').getFullList({
|
return await pb.collection('gifts_items').getFullList({
|
||||||
filter: `year = "${yearId}"`,
|
filter: `year = "${yearRecord.id}"`,
|
||||||
expand: 'year,person',
|
expand: 'year,person',
|
||||||
fields: '*,expand.year.year,expand.person.name,expand.year.id,expand.person.id',
|
fields: '*,expand.year.year,expand.person.name,expand.year.id,expand.person.id',
|
||||||
});
|
});
|
||||||
|
|||||||
2
src/lib/db/types.d.ts
vendored
2
src/lib/db/types.d.ts
vendored
@@ -25,7 +25,7 @@ namespace DB {
|
|||||||
link: string;
|
link: string;
|
||||||
cost: number;
|
cost: number;
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
status: 'planned' | 'decided' | 'bought' | 'wrapped';
|
status: 'planned' | 'decided' | 'bought' | 'ready' | 'wrapped';
|
||||||
created: Date;
|
created: Date;
|
||||||
updated: Date;
|
updated: Date;
|
||||||
|
|
||||||
|
|||||||
2
src/types/global.d.ts
vendored
2
src/types/global.d.ts
vendored
@@ -42,7 +42,7 @@ type Gift = {
|
|||||||
link: string;
|
link: string;
|
||||||
cost: number;
|
cost: number;
|
||||||
imageUrl: string;
|
imageUrl: string;
|
||||||
status: 'planned' | 'decided' | 'bought' | 'wrapped';
|
status: 'planned' | 'decided' | 'bought' | 'ready' | 'wrapped';
|
||||||
created: Date;
|
created: Date;
|
||||||
updated: Date;
|
updated: Date;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user