147 lines
4.8 KiB
TypeScript
147 lines
4.8 KiB
TypeScript
import { useState } from 'react';
|
||
import { format } from 'date-fns';
|
||
|
||
interface Props {
|
||
isOpen: boolean;
|
||
onClose: () => void;
|
||
onSave: (data: { date: string; time: string; description: string }) => Promise<unknown>;
|
||
}
|
||
|
||
export function AddLogModal({ isOpen, onClose, onSave }: Props) {
|
||
const [date, setDate] = useState(format(new Date(), 'yyyy-MM-dd'));
|
||
const [time, setTime] = useState('8');
|
||
const [description, setDescription] = useState('');
|
||
const [error, setError] = useState('');
|
||
const [isLoading, setIsLoading] = useState(false);
|
||
|
||
if (!isOpen) return null;
|
||
|
||
const handleSubmit = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
setError('');
|
||
setIsLoading(true);
|
||
|
||
try {
|
||
await onSave({ date, time, description });
|
||
setTime('8');
|
||
setDescription('');
|
||
onClose();
|
||
} catch (err) {
|
||
setError(err instanceof Error ? err.message : 'Ошибка');
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
|
||
const setQuickTime = (value: string) => {
|
||
setTime(value);
|
||
};
|
||
|
||
return (
|
||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||
<div className="bg-white rounded-lg p-6 w-full max-w-md mx-4">
|
||
<h2 className="text-xl font-semibold mb-4">Новая запись</h2>
|
||
|
||
<form onSubmit={handleSubmit} className="space-y-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Дата
|
||
</label>
|
||
<input
|
||
type="date"
|
||
value={date}
|
||
onChange={(e) => setDate(e.target.value)}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Часы
|
||
</label>
|
||
<div className="flex gap-2 mb-2">
|
||
<button
|
||
type="button"
|
||
onClick={() => setQuickTime('8')}
|
||
className={`px-3 py-1 rounded-md text-sm ${
|
||
time === '8'
|
||
? 'bg-blue-600 text-white'
|
||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||
}`}
|
||
>
|
||
8 ч
|
||
</button>
|
||
<button
|
||
type="button"
|
||
onClick={() => setQuickTime('4')}
|
||
className={`px-3 py-1 rounded-md text-sm ${
|
||
time === '4'
|
||
? 'bg-blue-600 text-white'
|
||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||
}`}
|
||
>
|
||
4 ч
|
||
</button>
|
||
<button
|
||
type="button"
|
||
onClick={() => setQuickTime('1')}
|
||
className={`px-3 py-1 rounded-md text-sm ${
|
||
time === '1'
|
||
? 'bg-blue-600 text-white'
|
||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||
}`}
|
||
>
|
||
1 ч
|
||
</button>
|
||
</div>
|
||
<input
|
||
type="text"
|
||
value={time}
|
||
onChange={(e) => setTime(e.target.value)}
|
||
placeholder="8, 8:30, 8,5"
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
required
|
||
/>
|
||
<p className="text-xs text-gray-500 mt-1">
|
||
Форматы: 8, 8:30, 8,5, 8.5
|
||
</p>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Описание
|
||
</label>
|
||
<textarea
|
||
value={description}
|
||
onChange={(e) => setDescription(e.target.value)}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none"
|
||
rows={3}
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
{error && <p className="text-red-500 text-sm">{error}</p>}
|
||
|
||
<div className="flex gap-3 pt-2">
|
||
<button
|
||
type="button"
|
||
onClick={onClose}
|
||
className="flex-1 py-2 px-4 border border-gray-300 text-gray-700 rounded-md hover:bg-gray-50"
|
||
>
|
||
Отмена
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
disabled={isLoading}
|
||
className="flex-1 py-2 px-4 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50"
|
||
>
|
||
{isLoading ? 'Сохранение...' : 'Сохранить'}
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|