'use client'; import { useState, useRef, useEffect, useMemo } from 'react'; import { useTranslations } from 'next-intl'; import { Plus, Tag, X } from 'lucide-react'; import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { useIdentityStore } from '@/stores/identity-store'; import { useSettingsStore } from '@/stores/settings-store'; import { generateSubAddress, extractDomain, suggestTagsForDomain, getTagValidationError, MAX_TAG_LENGTH, } from '@/lib/sub-addressing'; interface SubAddressHelperProps { baseEmail: string; recipientEmails: string[]; onSelectTag: (tag: string) => void; disabled?: boolean; } export function SubAddressHelper({ baseEmail, recipientEmails, onSelectTag, disabled = false, }: SubAddressHelperProps) { const t = useTranslations('identities.sub_address'); const [isOpen, setIsOpen] = useState(false); const [tag, setTag] = useState(''); const [error, setError] = useState(null); const popoverRef = useRef(null); const { subAddress, addRecentTag, addTagSuggestion } = useIdentityStore(); const subAddressDelimiter = useSettingsStore((state) => state.subAddressDelimiter); // Get suggestions based on recipient (memoized for performance) const suggestions = useMemo(() => { return recipientEmails .map(extractDomain) .filter(Boolean) .flatMap((domain) => suggestTagsForDomain(domain!)) .filter((tag, index, self) => self.indexOf(tag) === index) .slice(0, 5); }, [recipientEmails]); // Generate preview const preview = tag ? generateSubAddress(baseEmail, tag, subAddressDelimiter) : baseEmail; // Close popover when clicking outside useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if (popoverRef.current && !popoverRef.current.contains(e.target as Node)) { setIsOpen(false); } }; if (isOpen) { document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); } }, [isOpen]); const handleTagChange = (value: string) => { setTag(value); const errorCode = getTagValidationError(value); // Translate error code to localized message let errorMessage: string | null = null; if (errorCode === 'EMPTY') { errorMessage = t('validation.empty'); } else if (errorCode === 'TOO_LONG') { errorMessage = t('validation.too_long', { max: MAX_TAG_LENGTH }); } else if (errorCode === 'INVALID_CHARS') { errorMessage = t('validation.invalid_chars'); } setError(errorMessage); }; const handleSelectTag = (selectedTag: string) => { const errorCode = getTagValidationError(selectedTag); if (errorCode) { // Translate error code to localized message let errorMessage: string | null = null; if (errorCode === 'EMPTY') { errorMessage = t('validation.empty'); } else if (errorCode === 'TOO_LONG') { errorMessage = t('validation.too_long', { max: MAX_TAG_LENGTH }); } else if (errorCode === 'INVALID_CHARS') { errorMessage = t('validation.invalid_chars'); } setError(errorMessage); return; } // Add to recent tags and suggestions addRecentTag(selectedTag); const domain = recipientEmails.map(extractDomain).find(Boolean); if (domain) { addTagSuggestion(domain, selectedTag); } onSelectTag(selectedTag); setIsOpen(false); setTag(''); setError(null); }; const handleUseAddress = () => { if (!tag) return; handleSelectTag(tag); }; return (
{/* Trigger Button */} {/* Popover */} {isOpen && (
{/* Header */}

{t('popover_title')}

{/* Tag Input */}
handleTagChange(e.target.value)} placeholder={t('tag_input_placeholder')} className={cn(error && 'border-destructive')} autoFocus onKeyDown={(e) => { if (e.key === 'Enter' && tag && !error) { e.preventDefault(); handleUseAddress(); } }} /> {error && (

{error}

)}
{/* Preview */}
{t('preview_label')}
{preview}
{/* Recent Tags */} {subAddress.recentTags.length > 0 && (
{t('recent_tags')}
{subAddress.recentTags.slice(0, 5).map((recentTag) => ( ))}
)} {/* Suggested Tags */} {suggestions.length > 0 && (
{t('suggested_tags')}
{suggestions.map((suggestion) => ( ))}
)} {/* Help Text */}
{t('help_text', { delimiter: subAddressDelimiter })}
{/* Use Address Button */}
)}
); }