Enhance Badge component styling for better visibility, update GiftCard component to include Heading and FaTrashAlt icon for delete action, and refactor PersonCard to streamline layout and remove unnecessary state management.

This commit is contained in:
Norbert Maciaszek
2025-11-11 19:45:30 +01:00
parent 38f034b0e2
commit 924a58501a
6 changed files with 60 additions and 50 deletions

10
package-lock.json generated
View File

@@ -12,6 +12,7 @@
"pocketbase": "^0.26.3", "pocketbase": "^0.26.3",
"react": "19.2.0", "react": "19.2.0",
"react-dom": "19.2.0", "react-dom": "19.2.0",
"react-icons": "^5.5.0",
"zustand": "^5.0.8" "zustand": "^5.0.8"
}, },
"devDependencies": { "devDependencies": {
@@ -1556,6 +1557,15 @@
"react": "^19.2.0" "react": "^19.2.0"
} }
}, },
"node_modules/react-icons": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
"integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==",
"license": "MIT",
"peerDependencies": {
"react": "*"
}
},
"node_modules/scheduler": { "node_modules/scheduler": {
"version": "0.27.0", "version": "0.27.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",

View File

@@ -12,6 +12,7 @@
"pocketbase": "^0.26.3", "pocketbase": "^0.26.3",
"react": "19.2.0", "react": "19.2.0",
"react-dom": "19.2.0", "react-dom": "19.2.0",
"react-icons": "^5.5.0",
"zustand": "^5.0.8" "zustand": "^5.0.8"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -6,5 +6,9 @@ type Props = {
}; };
export const Badge: FC<Props> = ({ children, disabled }) => { export const Badge: FC<Props> = ({ children, disabled }) => {
return <span className='px-2 py-1 text-xs font-medium text-blue-600 bg-blue-50 rounded-lg'>{children}</span>; return (
<span className='px-3 py-1 text-xs font-medium text-gray-800 bg-amber-300 rounded-lg hover:bg-amber-400 transition-all duration-200 ease-in-out'>
{children}
</span>
);
}; };

View File

@@ -9,6 +9,8 @@ import { useRouter } from 'next/navigation';
import { Button } from '@/components/atoms/Button'; import { Button } from '@/components/atoms/Button';
import { Badge } from '@/components/atoms/Badge'; import { Badge } from '@/components/atoms/Badge';
import Link from 'next/link'; import Link from 'next/link';
import { Heading } from '@/components/atoms/Heading';
import { FaTrashAlt } from 'react-icons/fa';
type Props = { type Props = {
hideDetails?: boolean; hideDetails?: boolean;
@@ -20,6 +22,7 @@ export const GiftCard: FC<Gift & Props> = ({ hideDetails = false, editable = fal
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const year = useGlobalStore((s) => s.year); const year = useGlobalStore((s) => s.year);
const router = useRouter(); const router = useRouter();
editable = editable || !!gift.id;
const { title, description, cost, status, created, expand, link } = gift; const { title, description, cost, status, created, expand, link } = gift;
@@ -38,42 +41,30 @@ export const GiftCard: FC<Gift & Props> = ({ hideDetails = false, editable = fal
return ( return (
<> <>
<div <div
className={`rounded-2xl shadow-sm p-4 my-6 md:p-6 transition-all duration-200 ease-in-out ${ className={`rounded-2xl shadow-sm p-4 my-6 transition-all duration-200 ease-in-out ${bgByStatus[status]} ${
bgByStatus[status] editable ? 'cursor-pointer hover:shadow-md ' : ''
} ${editable ? 'cursor-pointer hover:shadow-md ' : ''}`} }`}
onClick={editable ? () => setIsOpen(true) : undefined}> onClick={editable ? () => setIsOpen(true) : undefined}>
<div className='flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4'> <div className='flex flex-col gap-2'>
<div className='flex-1'> <div className='flex justify-between items-center flex-wrap gap-2'>
<div className='flex items-center justify-between gap-3 mb-2'> <div className='flex items-center gap-2'>
<h3 className='text-lg font-semibold text-gray-800'>{title}</h3> <Heading size='small' spacing='none'>
{title}
</Heading>
{editable && ( {editable && (
<div className='ml-auto'> <FaTrashAlt
<Button size={14}
variant='danger' color='red'
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handleDelete(); handleDelete();
}}> }}
Usuń />
</Button>
</div>
)} )}
</div> </div>
{description && <p className='text-base text-gray-600 mb-3'>{description}</p>}
<div
className={`flex flex-wrap text-sm text-gray-500 ${editable ? 'flex-col gap-2' : 'items-center gap-4'}`}>
<span>Data dodania: {new Date(created).toLocaleDateString()}</span>
<span>Koszt: {formatCurrency(cost)}</span>
{!hideDetails && (
<>
<span></span>
<span>Dla: {expand?.person.name}</span>
</>
)}
<span>Status: {formatStatus(status)}</span>
</div>
{link && ( {link && (
<div className='flex flex-wrap items-center gap-2 mt-6'> <div className='flex flex-wrap items-center gap-2'>
{link.split('\n').map((line, index) => ( {link.split('\n').map((line, index) => (
<Link href={line} target='_blank' key={index} onClick={(e) => e.stopPropagation()}> <Link href={line} target='_blank' key={index} onClick={(e) => e.stopPropagation()}>
<Badge>Link {index + 1}</Badge> <Badge>Link {index + 1}</Badge>
@@ -82,6 +73,16 @@ export const GiftCard: FC<Gift & Props> = ({ hideDetails = false, editable = fal
</div> </div>
)} )}
</div> </div>
<div className='flex flex-wrap items-center gap-2'>
<span>Koszt: {formatCurrency(cost)}</span>
{!hideDetails && (
<>
<span></span>
<span>Dla: {expand?.person.name}</span>
</>
)}
</div>
{description && <p className='text-base text-gray-600'>{description}</p>}
</div> </div>
</div> </div>

View File

@@ -28,7 +28,7 @@ export const PersonCardEdit: FC<Props> = ({ person }) => {
<Button variant='primary' onClick={() => setIsOpen(true)}> <Button variant='primary' onClick={() => setIsOpen(true)}>
Edytuj Edytuj
</Button> </Button>
<Button variant='secondary' onClick={handleDelete}> <Button variant='danger' onClick={handleDelete}>
Usuń Usuń
</Button> </Button>
</div> </div>

View File

@@ -1,42 +1,36 @@
'use client';
import { Heading } from '@/components/atoms/Heading'; import { Heading } from '@/components/atoms/Heading';
import { formatCurrency } from '@/helpers/formatCurrency'; import { formatCurrency } from '@/helpers/formatCurrency';
import { FC, useState } from 'react'; import { FC } from 'react';
import { GiftCard } from '../GiftCard'; import { GiftCard } from '../GiftCard';
import { GiftCardAdd, PersonCardEdit } from './client'; import { GiftCardAdd, PersonCardEdit } from './client';
import { Button } from '@/components/atoms/Button';
export const PersonCard: FC<Person> = (person) => { export const PersonCard: FC<Person> = (person) => {
const [isOpen, setIsOpen] = useState(false);
const { name, notes, expand } = person; const { name, notes, expand } = person;
const { gifts } = expand; const { gifts } = expand;
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:flex-row sm:items-center sm:justify-between gap-4 pb-2 mb-6 border-b border-gray-200'> <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-1'> <Heading size='large' spacing='none'>
<Heading size='large'>{name}</Heading> {name}
</div> </Heading>
<PersonCardEdit person={person} /> <PersonCardEdit person={person} />
{notes && <p className='text-gray-600'>{notes}</p>}
</div> </div>
{notes && <p className='text-gray-600'>{notes}</p>}
<div className='flex flex-wrap items-center gap-4 text-sm text-gray-500'> <div className='flex flex-wrap items-center gap-4 text-sm text-gray-500'>
<span>Ilość prezentów: {gifts?.length || 0}</span> <span>Ilość prezentów: {gifts?.length || 0}</span>
<span></span> <span></span>
<span>Koszt: {formatCurrency(gifts?.reduce((acc, gift) => acc + gift.cost, 0) || 0)}</span> <span>Koszt: {formatCurrency(gifts?.reduce((acc, gift) => acc + gift.cost, 0) || 0)}</span>
</div> </div>
<div className='mt-8 mb-4 flex items-start gap-4'> <div className='mt-8 mb-4 flex items-start justify-between gap-4'>
<Heading size='medium' spacing='none'> <Heading size='medium' spacing='none'>
Prezenty <br /> Prezenty <br />
</Heading> </Heading>
{isOpen && <GiftCardAdd person={person} />} <GiftCardAdd person={person} />
<div className='ml-auto'>
<Button variant='secondary' onClick={() => setIsOpen(!isOpen)}>
{isOpen ? 'Mniej' : 'Więcej'}
</Button>
</div>
</div> </div>
{isOpen && gifts?.map((gift) => <GiftCard key={gift.id} {...gift} hideDetails editable personId={person.id} />)} {gifts?.map((gift) => (
<GiftCard key={gift.id} {...gift} hideDetails personId={person.id} />
))}
</div> </div>
); );
}; };