'use client'; import { formatErrorBanner } from '@/lib/api/toast-error'; import { useState, useEffect } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetFooter } from '@/components/ui/sheet'; import { TimezoneCombobox } from '@/components/shared/timezone-combobox'; import { apiFetch } from '@/lib/api/client'; // ISO-4217 currency shortlist for the port-currency dropdown. // Marina deals price in a small set; an admin who needs an exotic // currency can add it here without a schema change. const CURRENCY_OPTIONS: Array<{ value: string; label: string }> = [ { value: 'USD', label: 'USD — US Dollar' }, { value: 'EUR', label: 'EUR — Euro' }, { value: 'GBP', label: 'GBP — British Pound' }, { value: 'CHF', label: 'CHF — Swiss Franc' }, { value: 'AED', label: 'AED — UAE Dirham' }, { value: 'SAR', label: 'SAR — Saudi Riyal' }, { value: 'PLN', label: 'PLN — Polish Złoty' }, { value: 'AUD', label: 'AUD — Australian Dollar' }, { value: 'CAD', label: 'CAD — Canadian Dollar' }, { value: 'NZD', label: 'NZD — New Zealand Dollar' }, { value: 'JPY', label: 'JPY — Japanese Yen' }, ]; interface PortFormProps { open: boolean; onOpenChange: (open: boolean) => void; port?: { id: string; name: string; slug: string; logoUrl: string | null; primaryColor: string | null; defaultCurrency: string; timezone: string; isActive: boolean; } | null; onSuccess: () => void; } export function PortForm({ open, onOpenChange, port, onSuccess }: PortFormProps) { const [name, setName] = useState(''); const [slug, setSlug] = useState(''); const [primaryColor, setPrimaryColor] = useState('#0F4C81'); const [defaultCurrency, setDefaultCurrency] = useState('USD'); const [timezone, setTimezone] = useState('America/Anguilla'); const [isActive, setIsActive] = useState(true); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const isEdit = !!port; useEffect(() => { if (open) { if (port) { setName(port.name); setSlug(port.slug); setPrimaryColor(port.primaryColor ?? '#0F4C81'); setDefaultCurrency(port.defaultCurrency); setTimezone(port.timezone); setIsActive(port.isActive); } else { setName(''); setSlug(''); setPrimaryColor('#0F4C81'); setDefaultCurrency('USD'); setTimezone('America/Anguilla'); setIsActive(true); } setError(null); } }, [open, port]); function handleNameChange(value: string) { setName(value); if (!isEdit) { setSlug( value .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-|-$/g, ''), ); } } async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setError(null); setLoading(true); try { if (isEdit) { await apiFetch(`/api/v1/admin/ports/${port.id}`, { method: 'PATCH', body: { name, slug, primaryColor, defaultCurrency, timezone, isActive }, }); } else { await apiFetch('/api/v1/admin/ports', { method: 'POST', body: { name, slug, primaryColor, defaultCurrency, timezone }, }); } onSuccess(); onOpenChange(false); } catch (err: unknown) { const message = formatErrorBanner(err); setError(message); } finally { setLoading(false); } } return ( {isEdit ? 'Edit Port' : 'New Port'}
handleNameChange(e.target.value)} placeholder="Port Nimara" required />
setSlug(e.target.value)} placeholder="port-nimara" pattern="^[a-z0-9-]+$" required />

Used in URLs. Lowercase letters, numbers, and hyphens only.

setPrimaryColor(e.target.value)} className="h-9 w-9 rounded border cursor-pointer" /> setPrimaryColor(e.target.value)} placeholder="#0F4C81" className="w-28 font-mono text-sm" maxLength={7} />
setTimezone(tz ?? '')} />
{isEdit && (

Inactive ports are hidden from users

)} {error &&

{error}

}
); }