first commit

This commit is contained in:
Vadim Sobinin
2026-02-02 16:14:57 +03:00
commit fc886320e3
48 changed files with 5569 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
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>
);
}