185 lines
6.3 KiB
TypeScript
185 lines
6.3 KiB
TypeScript
'use client';
|
|
import { FC, useState, useEffect } from 'react';
|
|
import { Modal } from '@/components/atoms/Modal';
|
|
import { Button } from '@/components/atoms/Button';
|
|
import { formatStatus } from '@/helpers/formatStatus';
|
|
|
|
type Props = {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
gift?: Gift;
|
|
yearId: string;
|
|
personId?: string;
|
|
onSave: (data: {
|
|
title: string;
|
|
description: string;
|
|
link: string;
|
|
cost: number;
|
|
imageUrl: string;
|
|
status: 'planned' | 'decided' | 'bought' | 'wrapped';
|
|
year: string;
|
|
person: string;
|
|
}) => Promise<void>;
|
|
};
|
|
|
|
export const GiftModal: FC<Props> = ({ isOpen, onClose, gift, yearId, personId, onSave }) => {
|
|
const [title, setTitle] = useState(gift?.title || '');
|
|
const [description, setDescription] = useState(gift?.description || '');
|
|
const [link, setLink] = useState(gift?.link || '');
|
|
const [cost, setCost] = useState(gift?.cost || 0);
|
|
const [imageUrl, setImageUrl] = useState(gift?.imageUrl || '');
|
|
const [status, setStatus] = useState<Gift['status']>(gift?.status || 'planned');
|
|
const [selectedPersonId, setSelectedPersonId] = useState(personId || '');
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (gift) {
|
|
setTitle(gift.title);
|
|
setDescription(gift.description || '');
|
|
setLink(gift.link || '');
|
|
setCost(gift.cost);
|
|
setImageUrl(gift.imageUrl || '');
|
|
setStatus(gift.status);
|
|
setSelectedPersonId(gift.person);
|
|
} else {
|
|
setTitle('');
|
|
setDescription('');
|
|
setLink('');
|
|
setCost(0);
|
|
setImageUrl('');
|
|
setStatus('planned');
|
|
setSelectedPersonId(personId || '');
|
|
}
|
|
}, [gift, personId, isOpen]);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setIsLoading(true);
|
|
try {
|
|
await onSave({
|
|
title,
|
|
description,
|
|
link,
|
|
cost,
|
|
imageUrl,
|
|
status,
|
|
year: yearId,
|
|
person: selectedPersonId,
|
|
});
|
|
onClose();
|
|
} catch (error) {
|
|
console.error('Error saving gift:', error);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Modal
|
|
isOpen={isOpen}
|
|
onClose={onClose}
|
|
title={gift ? 'Edytuj prezent' : 'Dodaj nowy prezent'}
|
|
footer={
|
|
<div className='flex justify-end gap-3'>
|
|
<Button variant='secondary' onClick={onClose} disabled={isLoading}>
|
|
Anuluj
|
|
</Button>
|
|
<Button variant='primary' onClick={handleSubmit} disabled={isLoading || !title.trim() || !selectedPersonId}>
|
|
{isLoading ? 'Zapisywanie...' : 'Zapisz'}
|
|
</Button>
|
|
</div>
|
|
}>
|
|
<form onSubmit={handleSubmit} className='space-y-4'>
|
|
<div>
|
|
<label htmlFor='title' className='block text-sm font-medium text-gray-700 mb-2'>
|
|
Tytuł *
|
|
</label>
|
|
<input
|
|
id='title'
|
|
type='text'
|
|
value={title}
|
|
onChange={(e) => setTitle(e.target.value)}
|
|
className='w-full px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all'
|
|
required
|
|
disabled={isLoading}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label htmlFor='description' className='block text-sm font-medium text-gray-700 mb-2'>
|
|
Opis
|
|
</label>
|
|
<textarea
|
|
id='description'
|
|
value={description}
|
|
onChange={(e) => setDescription(e.target.value)}
|
|
rows={3}
|
|
className='w-full px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all resize-none'
|
|
disabled={isLoading}
|
|
/>
|
|
</div>
|
|
<div className='grid grid-cols-1 md:grid-cols-2 gap-4'>
|
|
<div>
|
|
<label htmlFor='cost' className='block text-sm font-medium text-gray-700 mb-2'>
|
|
Koszt (PLN) *
|
|
</label>
|
|
<input
|
|
id='cost'
|
|
type='number'
|
|
step='0.01'
|
|
min='0'
|
|
value={cost}
|
|
onChange={(e) => setCost(parseFloat(e.target.value) || 0)}
|
|
className='w-full px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all'
|
|
required
|
|
disabled={isLoading}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label htmlFor='status' className='block text-sm font-medium text-gray-700 mb-2'>
|
|
Status *
|
|
</label>
|
|
<select
|
|
id='status'
|
|
value={status}
|
|
onChange={(e) => setStatus(e.target.value as typeof status)}
|
|
className='w-full px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all'
|
|
required
|
|
disabled={isLoading}>
|
|
<option value='planned'>{formatStatus('planned')} </option>
|
|
<option value='decided'>{formatStatus('decided')}</option>
|
|
<option value='bought'>{formatStatus('bought')}</option>
|
|
<option value='wrapped'>{formatStatus('wrapped')}</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label htmlFor='link' className='block text-sm font-medium text-gray-700 mb-2'>
|
|
Link
|
|
</label>
|
|
<input
|
|
id='link'
|
|
type='url'
|
|
value={link}
|
|
onChange={(e) => setLink(e.target.value)}
|
|
className='w-full px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all'
|
|
disabled={isLoading}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label htmlFor='imageUrl' className='block text-sm font-medium text-gray-700 mb-2'>
|
|
URL obrazu
|
|
</label>
|
|
<input
|
|
id='imageUrl'
|
|
type='url'
|
|
value={imageUrl}
|
|
onChange={(e) => setImageUrl(e.target.value)}
|
|
className='w-full px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all'
|
|
disabled={isLoading}
|
|
/>
|
|
</div>
|
|
</form>
|
|
</Modal>
|
|
);
|
|
};
|