import { DataTable } from '@/components/ui'; import { PlusIcon, XMarkIcon } from '@heroicons/react/24/outline'; import { ColumnDef } from '@tanstack/react-table'; import { useCallback, useMemo, useState } from 'react'; import { useExchanges } from '../hooks/useExchanges'; import { Exchange, SourceMapping } from '../types'; import { AddSourceDialog } from './AddSourceDialog'; export function ExchangesTable() { const { exchanges, loading, error, updateExchange, addSource, removeSource } = useExchanges(); const [editingCell, setEditingCell] = useState<{ id: string; field: string } | null>(null); const [editValue, setEditValue] = useState(''); const [addSourceDialog, setAddSourceDialog] = useState<{ id: string; exchangeName: string; } | null>(null); const handleCellEdit = useCallback( async (id: string, field: string, value: string) => { if (field === 'shortName') { await updateExchange(id, { shortName: value }); } setEditingCell(null); setEditValue(''); }, [updateExchange] ); const handleToggleActive = useCallback( async (id: string, currentStatus: boolean) => { await updateExchange(id, { active: !currentStatus }); }, [updateExchange] ); const handleAddSource = useCallback(async (id: string, exchangeName: string) => { setAddSourceDialog({ id, exchangeName }); }, []); const handleRemoveSource = useCallback( async (exchangeId: string, sourceName: string) => { if (confirm(`Are you sure you want to remove the ${sourceName} source?`)) { await removeSource(exchangeId, sourceName); } }, [removeSource] ); const columns = useMemo[]>(() => { return [ { id: 'masterExchangeId', header: 'Master ID', accessorKey: 'masterExchangeId', size: 50, enableResizing: false, cell: ({ getValue, cell }) => ( {getValue() as string} ), }, { id: 'shortName', header: 'Short Name', accessorKey: 'shortName', size: 50, enableResizing: false, cell: ({ getValue, row, cell }) => { const isEditing = editingCell?.id === row.original._id && editingCell?.field === 'shortName'; if (isEditing) { return ( setEditValue(e.target.value)} onBlur={() => handleCellEdit(row.original._id, 'shortName', editValue)} onKeyDown={e => { if (e.key === 'Enter') { handleCellEdit(row.original._id, 'shortName', editValue); } else if (e.key === 'Escape') { setEditingCell(null); setEditValue(''); } }} className="w-full bg-surface border border-border rounded px-2 py-1 text-sm" autoFocus /> ); } return (
{ setEditingCell({ id: row.original._id, field: 'shortName' }); setEditValue(getValue() as string); }} > {getValue() as string}
); }, }, { id: 'officialName', header: 'Official Name', accessorKey: 'officialName', size: 150, maxSize: 150, enableResizing: true, cell: ({ getValue, cell }) => ( {getValue() as string} ), }, { id: 'country', header: 'Country', accessorKey: 'country', size: 40, maxSize: 40, cell: ({ getValue }) => ( {getValue() as string} ), }, { id: 'currency', header: 'Currency', accessorKey: 'currency', size: 40, cell: ({ getValue, cell }) => ( {getValue() as string} ), }, { id: 'active', header: 'Active', accessorKey: 'active', size: 80, maxSize: 80, cell: ({ getValue, row, cell }) => { const isActive = getValue() as boolean; return ( ); }, }, { id: 'sources', header: 'Sources', accessorKey: 'sourceMappings', minSize: 400, maxSize: 400, size: 400, enableResizing: true, cell: ({ getValue, row, cell }) => { const sourceMappings = getValue() as Record; const sources = Object.keys(sourceMappings); return (
{sources.map(source => { // The source key is already in format "source_sourcecode" from the storage const displayText = source.toUpperCase(); return ( {displayText} ); })}
); }, }, { id: 'updated_at', header: 'Last Updated', accessorKey: 'updated_at', size: 150, maxSize: 150, cell: ({ getValue }) => ( {new Date(getValue() as string).toLocaleDateString()} ), }, ]; }, [ editingCell, editValue, handleCellEdit, handleRemoveSource, handleToggleActive, handleAddSource, ]); if (error) { return (

Error Loading Exchanges

{error}

Make sure the data-service is running on localhost:2001

); } return ( <> {addSourceDialog && ( setAddSourceDialog(null)} onAddSource={async (sourceRequest: { source: string; source_code: string; mapping: { id: string; name: string; code: string; aliases: string[] }; }) => { const success = await addSource(addSourceDialog.id, sourceRequest); if (success) { setAddSourceDialog(null); } }} /> )} ); }