import { useState, useEffect, useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import apiClient from '../api/client';
import { useBusiness } from '../context/BusinessContext';
import RegisterBusinessModal from '../components/RegisterBusinessModal';
import { fetchActiveSalesChannels } from '../utils/fyndSalesChannels';
import {
getBusinessDomain,
getBusinessImage,
getBusinessName,
getBusinessTagline,
getChannelId,
} from '../utils/businessProfile';
function DeleteConfirmModal({ businessName, onCancel, onConfirm, deleting }) {
return (
đź—‘
Delete Business?
This will permanently delete {businessName} and all its events, templates, and images. This cannot be undone.
);
}
function BusinessCreatedModal({ business, onClose }) {
const name = getBusinessName(business);
const domain = getBusinessDomain(business);
const tagline = getBusinessTagline(business);
const image = getBusinessImage(business);
return (
âś“
Business Added!
Your business is ready for onboarding.
{image ? (

) : (
{name?.[0]?.toUpperCase() || 'B'}
)}
{name}
{domain &&
{domain}
}
{tagline &&
{tagline}
}
);
}
function SalesChannelCard({ channel, disabled, onImport }) {
const name = getBusinessName(channel);
const domain = getBusinessDomain(channel);
const image = getBusinessImage(channel);
return (
{image ? (

) : (
{name?.[0]?.toUpperCase() || 'S'}
)}
{name}
{domain || 'Domain unavailable'}
{channel.websiteUrl ? 'Ready to scrape' : 'Use manual URL fallback'}
);
}
export default function Businesses() {
const navigate = useNavigate();
const { setActiveBusiness } = useBusiness();
const [businesses, setBusinesses] = useState([]);
const [salesChannels, setSalesChannels] = useState([]);
const [loading, setLoading] = useState(true);
const [salesChannelsStatus, setSalesChannelsStatus] = useState('loading');
const [salesChannelsError, setSalesChannelsError] = useState('');
const [salesChannelQuery, setSalesChannelQuery] = useState('');
const [selectingBusinessId, setSelectingBusinessId] = useState('');
const [creatingSalesChannelId, setCreatingSalesChannelId] = useState('');
const [createdBusiness, setCreatedBusiness] = useState(null);
const [showModal, setShowModal] = useState(false);
const [deleteTarget, setDeleteTarget] = useState(null);
const [deleting, setDeleting] = useState(false);
const [error, setError] = useState('');
const configuredApplicationIds = useMemo(() => (
new Set(
businesses
.map((business) => String(business?.applicationId || '').trim())
.filter(Boolean)
)
), [businesses]);
const availableSalesChannels = useMemo(() => (
salesChannels.filter((channel) => !configuredApplicationIds.has(getChannelId(channel)))
), [configuredApplicationIds, salesChannels]);
const filteredSalesChannels = useMemo(() => {
const query = salesChannelQuery.trim().toLowerCase();
if (!query) return availableSalesChannels;
return availableSalesChannels.filter((channel) => {
const name = String(getBusinessName(channel) || '').toLowerCase();
const domain = String(getBusinessDomain(channel) || '').toLowerCase();
return name.includes(query) || domain.includes(query);
});
}, [availableSalesChannels, salesChannelQuery]);
const loadBusinesses = useCallback(async () => {
const res = await apiClient.get('/api/businesses');
setBusinesses(res.data.businesses || []);
}, []);
const loadSalesChannels = useCallback(async () => {
setSalesChannelsStatus('loading');
const channels = await fetchActiveSalesChannels();
setSalesChannels(channels);
setSalesChannelsStatus('success');
}, []);
const load = useCallback(async () => {
setLoading(true);
setError('');
setSalesChannelsError('');
try {
const [businessesRes, salesChannelsRes] = await Promise.allSettled([
loadBusinesses(),
loadSalesChannels(),
]);
if (businessesRes.status === 'rejected') {
setError('Failed to load businesses');
}
if (salesChannelsRes.status === 'rejected') {
setSalesChannels([]);
setSalesChannelsStatus('error');
setSalesChannelsError(
salesChannelsRes.reason?.message || 'Active sales channels could not be loaded right now.'
);
}
} finally {
setLoading(false);
}
}, [loadBusinesses, loadSalesChannels]);
useEffect(() => { load(); }, [load]);
async function handleSelect(biz) {
setSelectingBusinessId(biz.businessId);
setError('');
try {
const setupComplete = await setActiveBusiness(biz);
navigate(`/${biz.businessId}/${setupComplete ? 'events' : 'global-sms'}`);
} catch (err) {
setError(err.response?.data?.error || 'Failed to open business');
} finally {
setSelectingBusinessId('');
}
}
async function handleCreateFromSalesChannel(channel) {
const applicationId = getChannelId(channel);
if (!applicationId) return;
if (!channel.websiteUrl) {
setSalesChannelsError('A website URL could not be derived from this sales channel. Please use Add Business and enter the URL manually.');
return;
}
setCreatingSalesChannelId(applicationId);
setError('');
setSalesChannelsError('');
try {
const res = await apiClient.post('/api/businesses', {
applicationId,
websiteUrl: channel.websiteUrl,
});
setCreatedBusiness(res.data);
await Promise.all([loadBusinesses(), loadSalesChannels()]);
setSalesChannelsStatus('success');
} catch (err) {
setError(err.response?.data?.error || 'Failed to add business from sales channel');
} finally {
setCreatingSalesChannelId('');
}
}
async function handleDelete() {
if (!deleteTarget) return;
setDeleting(true);
try {
await apiClient.delete(`/api/businesses/${deleteTarget.businessId}`);
setDeleteTarget(null);
await load();
} catch (err) {
setError(err.response?.data?.error || 'Failed to delete business');
} finally {
setDeleting(false);
}
}
if (loading) {
return (
);
}
return (
{businesses.length > 0 ? 'Your Businesses' : 'Set Up Your First Business'}
Import from an active sales channel when available, or use the website URL fallback to scrape manually.
{error && (
{error}
)}
Configured Businesses
Select a business to manage its SMS templates.
{businesses.length > 0 ? (
{businesses.map((biz) => (
Click to manage →
))}
) : (
No configured businesses yet.
Import from an active sales channel below, or use Add Business to enter a storefront URL manually.
)}
Active Sales Channels
These are pulled directly from Commerce and can be scraped into businesses with one click.
{salesChannelsError && (
{salesChannelsError}
)}
{salesChannelsStatus === 'loading' ? (
Loading active sales channels…
) : availableSalesChannels.length > 0 ? (
setSalesChannelQuery(e.target.value)}
placeholder="Search by channel name or domain"
className="w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm text-gray-900 placeholder-gray-400 shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:border-transparent"
/>
{filteredSalesChannels.map((channel) => {
const channelId = getChannelId(channel);
return (
handleCreateFromSalesChannel(channel)}
/>
);
})}
{filteredSalesChannels.length === 0 && (
No active sales channels matched your search.
Use the website URL fallback if you want to scrape a storefront directly.
)}
) : (
No active sales channels are available right now.
Use Add Business to enter a website URL manually and keep moving.
)}
{showModal && (
{ setShowModal(false); loadBusinesses(); }} />
)}
{createdBusiness && setCreatedBusiness(null)} />}
{deleteTarget && (
setDeleteTarget(null)}
onConfirm={handleDelete}
deleting={deleting}
/>
)}
);
}