import React, { useState } from 'react'; import type { ManualCompetitor } from '../../service/visibility/visibility.interface'; import { addTrackedCompetitor } from '../../service/visibility/visibility.service'; import Modal from './Modal'; interface AddCompetitorModalProps { open: boolean; onClose: () => void; onAdded: (competitors: ManualCompetitor[]) => void; clientId: string; token: string; brandId: string; } /** * Modal that collects ``domain`` (required) and ``name`` (optional) and * POSTs to ``/brands/{id}/tracked-competitors``. The submission is * idempotent on the backend. */ const AddCompetitorModal = ({ open, onClose, onAdded, clientId, token, brandId, }: AddCompetitorModalProps): JSX.Element => { const [domain, setDomain] = useState(''); const [name, setName] = useState(''); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const reset = () => { setDomain(''); setName(''); setError(null); }; const handleClose = () => { reset(); onClose(); }; const handleSubmit = async () => { const trimmed = domain.trim().toLowerCase(); if (!trimmed) { setError('Domain is required.'); return; } setSubmitting(true); setError(null); try { const response = await addTrackedCompetitor(clientId, token, brandId, { domain: trimmed, name: name.trim() || undefined, }); onAdded(response.competitors); reset(); onClose(); } catch (err) { setError( err instanceof Error ? err.message : 'Failed to add competitor.' ); } finally { setSubmitting(false); } }; return ( } >
{error && (
{error}
)}
setDomain(e.target.value)} placeholder="rival.com" autoComplete="off" className="rounded-md border border-gray-300 px-3 py-1.5 text-sm" />

Canonical domain without protocol (e.g. rival.com).

setName(e.target.value)} placeholder="Display name" autoComplete="off" className="rounded-md border border-gray-300 px-3 py-1.5 text-sm" />

Falls back to the domain when left empty.

); }; export default AddCompetitorModal;