test
This commit is contained in:
parent
b6e2fd8c9e
commit
93542667e6
2 changed files with 127 additions and 54 deletions
|
|
@ -26,16 +26,9 @@ function CellWithTooltip({ children, className }: { children: React.ReactNode; c
|
|||
const textContent = element.textContent || '';
|
||||
setTooltipContent(textContent);
|
||||
|
||||
console.log('Element dimensions:', {
|
||||
scrollWidth: element.scrollWidth,
|
||||
clientWidth: element.clientWidth,
|
||||
textContent,
|
||||
isOverflowing: element.scrollWidth > element.clientWidth
|
||||
});
|
||||
|
||||
// Check if content is overflowing by comparing scroll width to client width
|
||||
const isOverflowing = element.scrollWidth > element.clientWidth;
|
||||
if (isOverflowing || textContent.length > 10) { // Temporarily show tooltip for testing
|
||||
if (isOverflowing && textContent.trim().length > 0) {
|
||||
setShowTooltip(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -174,9 +167,15 @@ export function DataTable<T>({
|
|||
maxWidth: `${cell.column.getSize()}px`,
|
||||
}}
|
||||
>
|
||||
<CellWithTooltip className="truncate overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</CellWithTooltip>
|
||||
{(cell.column.columnDef as any).disableTooltip ? (
|
||||
<div className="truncate overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</div>
|
||||
) : (
|
||||
<CellWithTooltip className="truncate overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</CellWithTooltip>
|
||||
)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { DataTable } from '@/components/ui';
|
||||
import { PlusIcon, XMarkIcon, CheckIcon } from '@heroicons/react/24/outline';
|
||||
import { PlusIcon, XMarkIcon, CheckIcon, TrashIcon } from '@heroicons/react/24/outline';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import { useCallback, useMemo, useState, useEffect } from 'react';
|
||||
import { useExchanges } from '../hooks/useExchanges';
|
||||
|
|
@ -28,8 +28,8 @@ export function ExchangesTable() {
|
|||
|
||||
const handleCellEdit = useCallback(
|
||||
async (id: string, field: string, value: string) => {
|
||||
if (field === 'name') {
|
||||
await updateExchange(id, { name: value });
|
||||
if (field === 'name' || field === 'code') {
|
||||
await updateExchange(id, { [field]: value });
|
||||
}
|
||||
setEditingCell(null);
|
||||
setEditValue('');
|
||||
|
|
@ -48,6 +48,16 @@ export function ExchangesTable() {
|
|||
setAddProviderDialog({ exchangeId, exchangeName });
|
||||
}, []);
|
||||
|
||||
const handleDeleteExchange = useCallback(async (exchangeId: string, exchangeName: string) => {
|
||||
if (confirm(`Are you sure you want to delete "${exchangeName}"? This will hide the exchange and make all its provider mappings available for remapping.`)) {
|
||||
const success = await updateExchange(exchangeId, { active: false });
|
||||
if (success) {
|
||||
// Optionally refresh the list or show a success message
|
||||
refetch();
|
||||
}
|
||||
}
|
||||
}, [updateExchange, refetch]);
|
||||
|
||||
const handleToggleProviderMapping = useCallback(
|
||||
async (mappingId: string, currentStatus: boolean) => {
|
||||
const success = await updateProviderMapping(mappingId, { active: !currentStatus });
|
||||
|
|
@ -94,28 +104,50 @@ export function ExchangesTable() {
|
|||
},
|
||||
size: 40,
|
||||
enableResizing: false,
|
||||
},
|
||||
{
|
||||
id: 'id',
|
||||
header: 'ID',
|
||||
accessorKey: 'id',
|
||||
size: 80,
|
||||
cell: ({ getValue }) => (
|
||||
<span className="font-mono text-primary-400 text-xs block truncate">
|
||||
{getValue() as string}
|
||||
</span>
|
||||
),
|
||||
disableTooltip: true,
|
||||
},
|
||||
{
|
||||
id: 'code',
|
||||
header: 'Code',
|
||||
accessorKey: 'code',
|
||||
size: 100,
|
||||
cell: ({ getValue }) => (
|
||||
<span className="font-mono text-text-primary text-sm font-medium block truncate">
|
||||
{getValue() as string}
|
||||
</span>
|
||||
),
|
||||
size: 120,
|
||||
cell: ({ getValue, row, cell }) => {
|
||||
const isEditing =
|
||||
editingCell?.id === row.original.id && editingCell?.field === 'code';
|
||||
|
||||
if (isEditing) {
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
value={editValue}
|
||||
onChange={e => setEditValue(e.target.value)}
|
||||
onBlur={() => handleCellEdit(row.original.id, 'code', editValue)}
|
||||
onKeyDown={e => {
|
||||
if (e.key === 'Enter') {
|
||||
handleCellEdit(row.original.id, 'code', editValue);
|
||||
} else if (e.key === 'Escape') {
|
||||
setEditingCell(null);
|
||||
setEditValue('');
|
||||
}
|
||||
}}
|
||||
className="w-full bg-surface border border-border rounded px-2 py-1 text-sm font-mono"
|
||||
autoFocus
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="cursor-pointer hover:bg-surface-secondary rounded px-2 py-1 transition-colors text-sm font-mono font-medium truncate overflow-hidden text-ellipsis whitespace-nowrap"
|
||||
onClick={() => {
|
||||
setEditingCell({ id: row.original.id, field: 'code' });
|
||||
setEditValue(getValue() as string);
|
||||
}}
|
||||
>
|
||||
{getValue() as string}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'name',
|
||||
|
|
@ -185,6 +217,7 @@ export function ExchangesTable() {
|
|||
header: 'Active',
|
||||
accessorKey: 'active',
|
||||
size: 80,
|
||||
disableTooltip: true,
|
||||
cell: ({ getValue, row }) => {
|
||||
const isActive = getValue() as boolean;
|
||||
return (
|
||||
|
|
@ -205,31 +238,58 @@ export function ExchangesTable() {
|
|||
header: 'Provider Mappings',
|
||||
accessorKey: 'provider_mapping_count',
|
||||
size: 180,
|
||||
disableTooltip: true,
|
||||
cell: ({ getValue, row }) => {
|
||||
const totalMappings = parseInt(getValue() as string) || 0;
|
||||
const activeMappings = parseInt(row.original.active_mapping_count) || 0;
|
||||
const verifiedMappings = parseInt(row.original.verified_mapping_count) || 0;
|
||||
const providers = row.original.providers;
|
||||
|
||||
// Parse provider mappings from the expanded data or from a summary field
|
||||
const exchangeId = row.original.id;
|
||||
const mappings = expandedRowData[exchangeId] || [];
|
||||
|
||||
const handleLoadMappings = async () => {
|
||||
if (mappings.length === 0 && totalMappings > 0) {
|
||||
const details = await fetchExchangeDetails(exchangeId);
|
||||
if (details) {
|
||||
setExpandedRowData(prev => ({
|
||||
...prev,
|
||||
[exchangeId]: details.provider_mappings
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<div
|
||||
className="flex flex-col gap-1 cursor-pointer"
|
||||
onMouseEnter={handleLoadMappings}
|
||||
>
|
||||
<div className="text-sm">
|
||||
<span className="text-text-primary font-medium">{totalMappings}</span>
|
||||
<span className="text-text-muted"> total</span>
|
||||
{activeMappings > 0 && (
|
||||
<span className="ml-2 text-green-400 text-xs">
|
||||
{activeMappings} active
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2 text-xs">
|
||||
<span className="text-green-400">
|
||||
<CheckIcon className="h-3 w-3 inline mr-1" />
|
||||
{activeMappings} active
|
||||
</span>
|
||||
<span className="text-blue-400">
|
||||
✓ {verifiedMappings} verified
|
||||
</span>
|
||||
</div>
|
||||
{providers && (
|
||||
<div className="text-xs text-text-muted truncate" title={providers}>
|
||||
{providers}
|
||||
{mappings.length > 0 ? (
|
||||
<div className="flex flex-wrap gap-1 text-xs">
|
||||
{mappings.slice(0, 3).map((mapping, index) => (
|
||||
<span key={index} className={`${mapping.active ? 'text-text-primary' : 'text-text-muted'}`}>
|
||||
<span className="font-bold text-primary-400">{mapping.provider.toLowerCase()}</span>
|
||||
<span className="text-text-secondary">({mapping.provider_exchange_code})</span>
|
||||
</span>
|
||||
))}
|
||||
{mappings.length > 3 && (
|
||||
<span className="text-text-muted">+{mappings.length - 3} more</span>
|
||||
)}
|
||||
</div>
|
||||
) : totalMappings > 0 ? (
|
||||
<div className="text-xs text-text-muted">Hover to load mappings...</div>
|
||||
) : (
|
||||
<div className="text-xs text-text-muted">No mappings</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
@ -238,16 +298,27 @@ export function ExchangesTable() {
|
|||
{
|
||||
id: 'actions',
|
||||
header: 'Actions',
|
||||
size: 120,
|
||||
size: 140,
|
||||
disableTooltip: true,
|
||||
cell: ({ row }) => (
|
||||
<button
|
||||
onClick={() => handleAddProviderMapping(row.original.id, row.original.name)}
|
||||
className="inline-flex items-center gap-1 px-2 py-1 bg-primary-500/20 text-primary-400 rounded text-xs hover:bg-primary-500/30 transition-colors"
|
||||
title="Add Provider Mapping"
|
||||
>
|
||||
<PlusIcon className="h-3 w-3" />
|
||||
Add Mapping
|
||||
</button>
|
||||
<div className="flex gap-1">
|
||||
<button
|
||||
onClick={() => handleAddProviderMapping(row.original.id, row.original.name)}
|
||||
className="inline-flex items-center gap-1 px-2 py-1 bg-primary-500/20 text-primary-400 rounded text-xs hover:bg-primary-500/30 transition-colors"
|
||||
title="Add Provider Mapping"
|
||||
>
|
||||
<PlusIcon className="h-3 w-3" />
|
||||
Add
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDeleteExchange(row.original.id, row.original.name)}
|
||||
className="inline-flex items-center gap-1 px-2 py-1 bg-danger/20 text-danger rounded text-xs hover:bg-danger/30 transition-colors"
|
||||
title="Delete Exchange"
|
||||
>
|
||||
<TrashIcon className="h-3 w-3" />
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
|
@ -265,10 +336,13 @@ export function ExchangesTable() {
|
|||
}, [
|
||||
editingCell,
|
||||
editValue,
|
||||
expandedRowData,
|
||||
handleCellEdit,
|
||||
handleToggleActive,
|
||||
handleAddProviderMapping,
|
||||
handleDeleteExchange,
|
||||
handleRowExpand,
|
||||
fetchExchangeDetails,
|
||||
]);
|
||||
|
||||
if (error) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue