import React, { useState } from 'react' import { AlertTriangle, CheckCircle2 } from 'lucide-react' interface HostPreview { domain_names: string name?: string forward_scheme?: string forward_host?: string forward_port?: number ssl_forced?: boolean websocket_support?: boolean [key: string]: unknown } interface ConflictDetail { existing: { forward_scheme: string forward_host: string forward_port: number ssl_forced: boolean websocket: boolean enabled: boolean } imported: { forward_scheme: string forward_host: string forward_port: number ssl_forced: boolean websocket: boolean } } interface Props { hosts: HostPreview[] conflicts: string[] conflictDetails?: Record errors: string[] caddyfileContent?: string onCommit: (resolutions: Record, names: Record) => Promise onCancel: () => void } export default function ImportReviewTable({ hosts, conflicts, conflictDetails, errors, caddyfileContent, onCommit, onCancel }: Props) { const [resolutions, setResolutions] = useState>(() => { const init: Record = {} conflicts.forEach((d: string) => { init[d] = 'keep' }) return init }) const [names, setNames] = useState>(() => { const init: Record = {} hosts.forEach((h) => { // Default name to domain name (first domain if comma-separated) init[h.domain_names] = h.name || h.domain_names.split(',')[0].trim() }) return init }) const [submitting, setSubmitting] = useState(false) const [error, setError] = useState(null) const [showSource, setShowSource] = useState(false) const [expandedRows, setExpandedRows] = useState>(new Set()) const handleCommit = async () => { // Validate all names are filled const emptyNames = hosts.filter(h => !names[h.domain_names]?.trim()) if (emptyNames.length > 0) { setError(`Please provide a name for all hosts. Missing: ${emptyNames.map(h => h.domain_names).join(', ')}`) return } setSubmitting(true) setError(null) try { await onCommit(resolutions, names) } catch (err) { setError(err instanceof Error ? err.message : 'Failed to commit import') } finally { setSubmitting(false) } } return (
{caddyfileContent && (
setShowSource(!showSource)}>

Source Caddyfile Content

{showSource ? 'Hide' : 'Show'}
{showSource && (
{caddyfileContent}
)}
)}

Review Imported Hosts

{error && (
{error}
)} {errors?.length > 0 && (
Issues found during parsing
    {errors.map((e, i) => (
  • {e}
  • ))}
)}
{hosts.map((h) => { const domain = h.domain_names const hasConflict = conflicts.includes(domain) const isExpanded = expandedRows.has(domain) const details = conflictDetails?.[domain] return ( {hasConflict && isExpanded && details && ( )} ) })}
Name Domain Names Status Conflict Resolution
setNames({ ...names, [domain]: e.target.value })} placeholder="Enter name" className={`w-full bg-gray-900 border rounded px-3 py-1.5 text-sm text-white focus:outline-none focus:ring-2 focus:ring-blue-500 ${ !names[domain]?.trim() ? 'border-red-500' : 'border-gray-700' }`} />
{hasConflict && ( )}
{domain}
{hasConflict ? ( Conflict ) : ( New )} {hasConflict ? ( ) : ( Will be imported )}
{/* Existing Configuration */}

Current Configuration

Target:
{details.existing.forward_scheme}://{details.existing.forward_host}:{details.existing.forward_port}
SSL Forced:
{details.existing.ssl_forced ? 'Yes' : 'No'}
WebSocket:
{details.existing.websocket ? 'Enabled' : 'Disabled'}
Status:
{details.existing.enabled ? 'Enabled' : 'Disabled'}
{/* Imported Configuration */}

Imported Configuration

Target:
{details.imported.forward_scheme}://{details.imported.forward_host}:{details.imported.forward_port}
SSL Forced:
{details.imported.ssl_forced ? 'Yes' : 'No'}
WebSocket:
{details.imported.websocket ? 'Enabled' : 'Disabled'}
Status:
(Imported hosts are disabled by default)
{/* Recommendation */}

💡 Recommendation:{' '} {getRecommendation(details)}

) } function getRecommendation(details: ConflictDetail): string { const hasTargetChange = details.imported.forward_host !== details.existing.forward_host || details.imported.forward_port !== details.existing.forward_port || details.imported.forward_scheme !== details.existing.forward_scheme const hasConfigChange = details.imported.ssl_forced !== details.existing.ssl_forced || details.imported.websocket !== details.existing.websocket if (hasTargetChange) { return 'The imported configuration points to a different backend server. Choose "Replace" if you want to update the target, or "Keep Existing" if the current setup is correct.' } if (hasConfigChange) { return 'The imported configuration has different SSL or WebSocket settings. Choose "Replace" to update these settings, or "Keep Existing" to maintain current configuration.' } return 'The configurations are identical. You can safely keep the existing configuration.' }