'use client' import { useState, useEffect } from 'react' import { logger } from '@/lib/utils/logger' import { Copy, Check } from '@phosphor-icons/react' import { listSites, Site } from '@/lib/api/sites' import { Select, Input, Button } from '@ciphera-net/ui' interface UtmBuilderProps { initialSiteId?: string } export default function UtmBuilder({ initialSiteId }: UtmBuilderProps) { const [sites, setSites] = useState([]) const [selectedSiteId, setSelectedSiteId] = useState(initialSiteId || '') const [values, setValues] = useState({ url: '', source: '', medium: '', campaign: '', term: '', content: '' }) const [copied, setCopied] = useState(false) // 1. Fetch sites on mount useEffect(() => { async function fetchSites() { try { const data = await listSites() setSites(data) } catch (e) { logger.error('Failed to load sites for UTM builder', e) } } fetchSites() }, []) // 2. Initialize default selection useEffect(() => { if (sites.length === 0) return if (initialSiteId) { const site = sites.find(s => s.id === initialSiteId) if (site && selectedSiteId !== site.id) { setSelectedSiteId(site.id) setValues(v => ({ ...v, url: `https://${site.domain}` })) } } else if (!selectedSiteId && !values.url) { const firstSite = sites[0] setSelectedSiteId(firstSite.id) setValues(v => ({ ...v, url: `https://${firstSite.domain}` })) } }, [sites, initialSiteId, selectedSiteId, values.url]) // 2. Handle Site Selection const handleSiteChange = (siteId: string) => { setSelectedSiteId(siteId) const site = sites.find(s => s.id === siteId) if (site) { setValues(prev => ({ ...prev, url: `https://${site.domain}` // Reset URL to base domain of selected site })) } } const generatedUrl = (() => { if (!values.url || !values.source || !values.medium || !values.campaign) return '' try { const url = new URL(values.url) url.searchParams.set('utm_source', values.source.toLowerCase()) url.searchParams.set('utm_medium', values.medium.toLowerCase()) url.searchParams.set('utm_campaign', values.campaign.toLowerCase()) if (values.term) url.searchParams.set('utm_term', values.term) if (values.content) url.searchParams.set('utm_content', values.content) return url.toString() } catch (e) { return '' } })() const copyToClipboard = () => { if (!generatedUrl) return navigator.clipboard.writeText(generatedUrl) setCopied(true) setTimeout(() => setCopied(false), 2000) } const handleChange = (e: React.ChangeEvent) => { setValues({ ...values, [e.target.name]: e.target.value }) } // Helper to handle path changes when a site is selected const handlePathChange = (e: React.ChangeEvent) => { const site = sites.find(s => s.id === selectedSiteId) if (!site) return const path = e.target.value // Ensure path starts with / if not empty const safePath = path.startsWith('/') || path === '' ? path : `/${path}` setValues(v => ({ ...v, url: `https://${site.domain}${safePath}` })) } // Extract path from current URL if site is selected const getCurrentPath = () => { const site = sites.find(s => s.id === selectedSiteId) if (!site || !values.url) return '' try { const urlObj = new URL(values.url) return urlObj.pathname === '/' ? '' : urlObj.pathname } catch { return '' } } const selectedSite = sites.find(s => s.id === selectedSiteId) return (
{/* Site Selector */} {sites.length > 0 && (
) : ( )}

You can add specific paths (e.g., /blog/post-1) to the URL above.

{generatedUrl && (
{generatedUrl}
)} ) }