diff --git a/client/src/components/RegisterBusinessModal.jsx b/client/src/components/RegisterBusinessModal.jsx index f38640c..a11e5e3 100644 --- a/client/src/components/RegisterBusinessModal.jsx +++ b/client/src/components/RegisterBusinessModal.jsx @@ -1,19 +1,102 @@ -import { useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import apiClient from '../api/client'; +function normalizeChannelsPayload(data) { + if (Array.isArray(data)) return data; + if (Array.isArray(data?.salesChannels)) return data.salesChannels; + if (Array.isArray(data?.channels)) return data.channels; + return []; +} + +function getChannelId(channel) { + return channel?.salesChannelId || channel?.id || channel?._id || ''; +} + export default function RegisterBusinessModal({ onClose }) { const [url, setUrl] = useState(''); const [status, setStatus] = useState('idle'); // idle | loading | success | error const [brandName, setBrandName] = useState(''); const [error, setError] = useState(''); + const [entryMode, setEntryMode] = useState('sales-channel'); + const [salesChannels, setSalesChannels] = useState([]); + const [salesChannelsStatus, setSalesChannelsStatus] = useState('loading'); // loading | success | error + const [salesChannelsError, setSalesChannelsError] = useState(''); + const [selectedSalesChannelId, setSelectedSalesChannelId] = useState(''); + const [searchQuery, setSearchQuery] = useState(''); + + useEffect(() => { + let cancelled = false; + + async function fetchSalesChannels() { + setSalesChannelsStatus('loading'); + setSalesChannelsError(''); + try { + const res = await apiClient.get('/api/businesses/sales-channels'); + if (cancelled) return; + + const channels = normalizeChannelsPayload(res.data); + setSalesChannels(channels); + + if (channels.length > 0) { + setSelectedSalesChannelId(getChannelId(channels[0])); + setSalesChannelsStatus('success'); + setEntryMode('sales-channel'); + } else { + setSalesChannelsStatus('error'); + setSalesChannelsError('No sales channels were found for this company yet. You can still enter the website URL manually.'); + setEntryMode('manual'); + } + } catch (err) { + if (cancelled) return; + setSalesChannels([]); + setSalesChannelsStatus('error'); + setSalesChannelsError(err.response?.data?.error || 'Sales channels could not be fetched right now. You can still enter the website URL manually.'); + setEntryMode('manual'); + } + } + + fetchSalesChannels(); + return () => { + cancelled = true; + }; + }, []); + + const filteredSalesChannels = useMemo(() => { + if (!searchQuery.trim()) return salesChannels; + const query = searchQuery.trim().toLowerCase(); + return salesChannels.filter((channel) => { + const name = String(channel?.name || '').toLowerCase(); + const domain = String(channel?.domain || '').toLowerCase(); + return name.includes(query) || domain.includes(query); + }); + }, [salesChannels, searchQuery]); + + const canUseSalesChannels = salesChannelsStatus === 'success' && salesChannels.length > 0; + const isSalesChannelMode = entryMode === 'sales-channel' && canUseSalesChannels; + const effectiveSelectedSalesChannelId = filteredSalesChannels.some( + (channel) => getChannelId(channel) === selectedSalesChannelId + ) + ? selectedSalesChannelId + : (filteredSalesChannels[0] ? getChannelId(filteredSalesChannels[0]) : ''); + const submitDisabled = status === 'loading' + || (isSalesChannelMode ? !effectiveSelectedSalesChannelId : !url.trim()); async function handleSubmit(e) { e.preventDefault(); - if (!url.trim()) return; + + const isSalesChannelMode = entryMode === 'sales-channel' && salesChannelsStatus === 'success'; + if (isSalesChannelMode && !effectiveSelectedSalesChannelId) return; + if (!isSalesChannelMode && !url.trim()) return; + setStatus('loading'); setError(''); + try { - const res = await apiClient.post('/api/businesses', { websiteUrl: url.trim() }); + const payload = isSalesChannelMode + ? { salesChannelId: effectiveSelectedSalesChannelId } + : { websiteUrl: url.trim() }; + + const res = await apiClient.post('/api/businesses', payload); setBrandName(res.data.brandName); setStatus('success'); } catch (err) { @@ -26,7 +109,6 @@ export default function RegisterBusinessModal({ onClose }) {
- {/* Success */} {status === 'success' && (
@@ -42,30 +124,111 @@ export default function RegisterBusinessModal({ onClose }) {
)} - {/* Form */} {(status === 'idle' || status === 'loading' || status === 'error') && ( <>

Add a Business

- Enter your website URL. We'll scrape your site and extract brand context to generate TRAI-compliant SMS templates. + Choose a sales channel first when available. If that lookup fails, you can still enter the website URL and we'll scrape it directly.

-
- - setUrl(e.target.value)} - placeholder="https://yourstore.com" +
+ +
+ {isSalesChannelMode ? ( +
+
+ + setSearchQuery(e.target.value)} + placeholder="Search by name or domain" + disabled={status === 'loading' || salesChannelsStatus === 'loading'} + className="w-full px-4 py-2.5 rounded-lg bg-white border border-gray-300 text-gray-900 placeholder-gray-400 font-medium focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:border-transparent transition disabled:opacity-50 text-sm shadow-sm" + /> +
+ +
+ + {filteredSalesChannels.length > 0 ? ( + + ) : ( +
+ No sales channels matched your search. +
+ )} +
+ + {salesChannelsStatus === 'loading' && ( +

+ Loading sales channels for this company… +

+ )} + + {salesChannelsError && ( +

+ {salesChannelsError} +

+ )} +
+ ) : ( +
+ + setUrl(e.target.value)} + placeholder="https://yourstore.com" + disabled={status === 'loading'} + className="w-full px-4 py-2.5 rounded-lg bg-white border border-gray-300 text-gray-900 placeholder-gray-400 font-medium focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:border-transparent transition disabled:opacity-50 text-sm shadow-sm" + required={!isSalesChannelMode} + /> + {salesChannelsError && ( +

+ {salesChannelsError} +

+ )} +
+ )} + {status === 'error' && (

{error}

)} @@ -81,7 +244,7 @@ export default function RegisterBusinessModal({ onClose }) {