Initial commit
This commit is contained in:
130
components/AddServiceModal.tsx
Normal file
130
components/AddServiceModal.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
|
||||
import React, { useState, useCallback, useRef, useEffect } from 'react';
|
||||
import { type Service } from '../types';
|
||||
import { generateServiceDescription } from '../services/geminiService';
|
||||
import { Globe, Loader2 } from 'lucide-react';
|
||||
|
||||
interface AddServiceModalProps {
|
||||
onClose: () => void;
|
||||
onAddService: (service: Omit<Service, 'id'>) => void;
|
||||
}
|
||||
|
||||
const AddServiceModal: React.FC<AddServiceModalProps> = ({ onClose, onAddService }) => {
|
||||
const [name, setName] = useState('');
|
||||
const [url, setUrl] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const [isFetchingDesc, setIsFetchingDesc] = useState(false);
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (modalRef.current && !modalRef.current.contains(event.target as Node)) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
};
|
||||
}, [onClose]);
|
||||
|
||||
const handleFetchDescription = useCallback(async () => {
|
||||
if (name.trim().length > 2 && process.env.API_KEY) {
|
||||
setIsFetchingDesc(true);
|
||||
const desc = await generateServiceDescription(name);
|
||||
setDescription(desc);
|
||||
setIsFetchingDesc(false);
|
||||
}
|
||||
}, [name]);
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (name && url) {
|
||||
onAddService({
|
||||
name,
|
||||
url,
|
||||
description: description || `A local service at ${url}`,
|
||||
icon: Globe,
|
||||
});
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-70 z-50 flex justify-center items-center p-4">
|
||||
<div ref={modalRef} className="bg-gray-800 rounded-lg shadow-xl w-full max-w-md p-6 border border-gray-700">
|
||||
<h2 className="text-2xl font-bold mb-6 text-white">Add a New Service</h2>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="name" className="block text-sm font-medium text-gray-300">
|
||||
Service Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
onBlur={handleFetchDescription}
|
||||
className="mt-1 block w-full bg-gray-900 border border-gray-600 rounded-md shadow-sm py-2 px-3 text-white focus:outline-none focus:ring-teal-500 focus:border-teal-500 sm:text-sm"
|
||||
placeholder="e.g., Home Assistant"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="url" className="block text-sm font-medium text-gray-300">
|
||||
URL (including http/https)
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="url"
|
||||
value={url}
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
className="mt-1 block w-full bg-gray-900 border border-gray-600 rounded-md shadow-sm py-2 px-3 text-white focus:outline-none focus:ring-teal-500 focus:border-teal-500 sm:text-sm"
|
||||
placeholder="http://192.168.1.100:8123"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="description" className="block text-sm font-medium text-gray-300">
|
||||
Description
|
||||
</label>
|
||||
<div className="relative">
|
||||
<textarea
|
||||
id="description"
|
||||
rows={3}
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
className="mt-1 block w-full bg-gray-900 border border-gray-600 rounded-md shadow-sm py-2 px-3 text-white focus:outline-none focus:ring-teal-500 focus:border-teal-500 sm:text-sm"
|
||||
placeholder="A short description of the service..."
|
||||
/>
|
||||
{isFetchingDesc && (
|
||||
<div className="absolute inset-y-0 right-3 flex items-center">
|
||||
<Loader2 className="h-5 w-5 text-teal-400 animate-spin" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-8 flex justify-end space-x-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 text-sm font-medium text-gray-300 bg-gray-700 rounded-md hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-gray-500"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="px-4 py-2 text-sm font-medium text-white bg-teal-500 rounded-md shadow-sm hover:bg-teal-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-teal-500"
|
||||
>
|
||||
Add Service
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddServiceModal;
|
||||
Reference in New Issue
Block a user