Refactor PlatformLineChart and Tabs components for improved layout and styling, enhance WhitelistSettings with responsive design, and implement API candles health check in HealthChecks. Update global styles for scrollbar visibility and adjust tool tabs for better organization.

This commit is contained in:
2025-11-08 04:56:41 +07:00
parent 83d13bde74
commit 044ffcc6f5
10 changed files with 285 additions and 221 deletions

View File

@@ -265,13 +265,18 @@ function PlatformLineChart({
color: theme['base-content'] || '#ffffff' color: theme['base-content'] || '#ffffff'
}, },
bgcolor: 'rgba(0,0,0,0)', bgcolor: 'rgba(0,0,0,0)',
bordercolor: 'rgba(0,0,0,0)' bordercolor: 'rgba(0,0,0,0)',
orientation: 'h' as const,
x: 0.5,
y: -0.15,
xanchor: 'center' as const,
yanchor: 'top' as const
}, },
margin: { margin: {
l: 60, l: 60,
r: 60, r: 60,
t: 60, t: 60,
b: 60, b: 80,
pad: 4 pad: 4
}, },
paper_bgcolor: 'rgba(0,0,0,0)', paper_bgcolor: 'rgba(0,0,0,0)',
@@ -290,9 +295,8 @@ function PlatformLineChart({
} }
const config: Partial<Config> = { const config: Partial<Config> = {
displayModeBar: true, displayModeBar: false,
displaylogo: false, displaylogo: false,
modeBarButtonsToRemove: ['pan2d', 'lasso2d', 'select2d', 'autoScale2d', 'resetScale2d'] as any,
responsive: true responsive: true
} }

View File

@@ -27,11 +27,12 @@ const Tabs: FC<ITabsProps> = ({
orientation === 'vertical' ? className + ' vertical' : className orientation === 'vertical' ? className + ' vertical' : className
} }
> >
<div className="tabs" role="tablist" aria-orientation={orientation}> <div className="overflow-x-auto scrollbar-hide -mx-4 px-4">
<div className="tabs flex-nowrap min-w-max" role="tablist" aria-orientation={orientation}>
{tabs.map((tab: any) => ( {tabs.map((tab: any) => (
<button <button
className={ className={
'mb-5 tab tab-bordered ' + 'mb-5 tab tab-bordered whitespace-nowrap flex-shrink-0 ' +
(selectedTab === tab.index ? 'tab-active' : '') (selectedTab === tab.index ? 'tab-active' : '')
} }
onClick={() => onClick(tab.index)} onClick={() => onClick(tab.index)}
@@ -48,7 +49,7 @@ const Tabs: FC<ITabsProps> = ({
))} ))}
{addButton && ( {addButton && (
<button <button
className="tab tab-bordered mb-5" className="tab tab-bordered mb-5 whitespace-nowrap flex-shrink-0"
onClick={onAddButton} onClick={onAddButton}
key={'add'} key={'add'}
type="button" type="button"
@@ -62,6 +63,7 @@ const Tabs: FC<ITabsProps> = ({
</button> </button>
)} )}
</div> </div>
</div>
<div <div
role="tabpanel" role="tabpanel"
aria-labelledby={`btn-${selectedTab}`} aria-labelledby={`btn-${selectedTab}`}

View File

@@ -72,27 +72,27 @@ const WhitelistSettings: React.FC = () => {
Search Filters Search Filters
</div> </div>
<div className="collapse-content"> <div className="collapse-content">
<div className="flex gap-4 pt-4"> <div className="flex gap-2 pt-2">
<div className="form-control w-full max-w-xs"> <div className="form-control w-full max-w-[200px]">
<label className="label"> <label className="label py-1">
<span className="label-text">Search by Ethereum Account</span> <span className="label-text text-xs">Search by Ethereum Account</span>
</label> </label>
<input <input
type="text" type="text"
placeholder="Enter Ethereum address..." placeholder="Enter Ethereum address..."
className="input input-bordered w-full" className="input input-bordered input-sm w-full"
value={searchExternalEthereumAccount} value={searchExternalEthereumAccount}
onChange={(e) => handleSearchExternalEthereum(e.target.value)} onChange={(e) => handleSearchExternalEthereum(e.target.value)}
/> />
</div> </div>
<div className="form-control w-full max-w-xs"> <div className="form-control w-full max-w-[200px]">
<label className="label"> <label className="label py-1">
<span className="label-text">Search by Twitter Account</span> <span className="label-text text-xs">Search by Twitter Account</span>
</label> </label>
<input <input
type="text" type="text"
placeholder="Enter Twitter handle..." placeholder="Enter Twitter handle..."
className="input input-bordered w-full" className="input input-bordered input-sm w-full"
value={searchTwitterAccount} value={searchTwitterAccount}
onChange={(e) => handleSearchTwitter(e.target.value)} onChange={(e) => handleSearchTwitter(e.target.value)}
/> />

View File

@@ -3,8 +3,7 @@ import BundleRequestsTable from './bundleRequestsTable';
const BacktestBundleForm: React.FC = () => { const BacktestBundleForm: React.FC = () => {
return ( return (
<div className="p-10 max-w-7xl mx-auto"> <div className="p-4 md:p-6 lg:p-8 w-full">
<h2 className="text-2xl font-bold mb-6">Bundle Backtest</h2>
<BundleRequestsTable /> <BundleRequestsTable />
</div> </div>
); );

View File

@@ -135,7 +135,7 @@ function PlatformSummary({index}: { index: number }) {
<div className="grid grid-cols-2 lg:grid-cols-4 gap-6 mb-8"> <div className="grid grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
{/* Total Volume Traded */} {/* Total Volume Traded */}
<div className="card bg-base-200"> <div className="card bg-base-200">
<div className="card-body"> <div className="card-body p-3 md:p-6">
<h3 className="card-title text-base-content/70">Total Volume Traded</h3> <h3 className="card-title text-base-content/70">Total Volume Traded</h3>
<div className="text-3xl font-bold text-base-content mb-1"> <div className="text-3xl font-bold text-base-content mb-1">
{formatCurrency(platformData?.totalPlatformVolume || 0)} {formatCurrency(platformData?.totalPlatformVolume || 0)}
@@ -189,36 +189,36 @@ function PlatformSummary({index}: { index: number }) {
{/* Top 3 Most Profitable */} {/* Top 3 Most Profitable */}
<div className="card bg-base-200"> <div className="card bg-base-200">
<div className="card-body"> <div className="card-body p-3 md:p-6">
<div className="flex items-center gap-2 mb-4"> <div className="flex items-center gap-2 mb-3 md:mb-4">
<span className="text-2xl">🤑</span> <span className="text-xl md:text-2xl">🤑</span>
<h3 className="card-title text-base-content/70">Top 3 Most Profitable</h3> <h3 className="card-title text-base-content/70 text-sm md:text-base">Top 3 Most Profitable</h3>
</div> </div>
<div className="space-y-3"> <div className="space-y-2 md:space-y-3">
{topStrategies?.topStrategies?.slice(0, 3).map((strategy, index) => ( {topStrategies?.topStrategies?.slice(0, 3).map((strategy, index) => (
<div key={index} className="flex items-center justify-between"> <div key={index} className="flex items-center justify-between gap-1 md:gap-2">
<div className="flex items-center gap-3"> <div className="flex items-center gap-1.5 md:gap-3 min-w-0 flex-1">
<div className="avatar placeholder"> <div className="avatar placeholder flex-shrink-0 hidden md:flex">
<div className="bg-primary text-primary-content rounded-full w-8"> <div className="bg-primary text-primary-content rounded-full w-8 h-8">
<span className="text-xs font-bold"> <span className="text-xs font-bold">
{strategy.strategyName?.charAt(0) || 'S'} {strategy.strategyName?.charAt(0) || 'S'}
</span> </span>
</div> </div>
</div> </div>
<div> <div className="min-w-0 flex-1">
<div className="text-sm text-base-content font-medium"> <div className="text-xs md:text-sm text-base-content font-medium truncate">
{strategy.strategyName || '[Strategy Name]'} {strategy.strategyName || '[Strategy Name]'}
</div> </div>
<div className="text-xs text-base-content/60">📧</div> <div className="text-[10px] md:text-xs text-base-content/60">📧</div>
</div> </div>
</div> </div>
<div <div
className={`text-sm font-bold ${strategy.netPnL && strategy.netPnL >= 0 ? 'text-success' : 'text-error'}`}> className={`text-xs md:text-sm font-bold flex-shrink-0 ${strategy.netPnL && strategy.netPnL >= 0 ? 'text-success' : 'text-error'}`}>
{strategy.netPnL && strategy.netPnL >= 0 ? '+' : ''}{formatCurrency(strategy.netPnL || 0)} {strategy.netPnL && strategy.netPnL >= 0 ? '+' : ''}{formatCurrency(strategy.netPnL || 0)}
</div> </div>
</div> </div>
)) || ( )) || (
<div className="text-base-content/60 text-sm">No profitable strategies found</div> <div className="text-base-content/60 text-xs md:text-sm">No profitable strategies found</div>
)} )}
</div> </div>
</div> </div>
@@ -226,44 +226,44 @@ function PlatformSummary({index}: { index: number }) {
{/* Top 3 Rising (by ROI) */} {/* Top 3 Rising (by ROI) */}
<div className="card bg-base-200"> <div className="card bg-base-200">
<div className="card-body"> <div className="card-body p-3 md:p-6">
<div className="flex items-center gap-2 mb-4"> <div className="flex items-center gap-2 mb-3 md:mb-4">
<span className="text-2xl">📈</span> <span className="text-xl md:text-2xl">📈</span>
<h3 className="card-title text-base-content/70">Top 3 by ROI</h3> <h3 className="card-title text-base-content/70 text-sm md:text-base">Top 3 by ROI</h3>
</div> </div>
<div className="space-y-3"> <div className="space-y-2 md:space-y-3">
{topStrategiesByRoi?.topStrategiesByRoi?.slice(0, 3).map((strategy, index) => ( {topStrategiesByRoi?.topStrategiesByRoi?.slice(0, 3).map((strategy, index) => (
<div key={index} className="flex items-center justify-between"> <div key={index} className="flex items-center justify-between gap-1 md:gap-2">
<div className="flex items-center gap-3"> <div className="flex items-center gap-1.5 md:gap-3 min-w-0 flex-1">
<div className="avatar placeholder"> <div className="avatar placeholder flex-shrink-0 hidden md:flex">
<div className="bg-success text-success-content rounded-full w-8"> <div className="bg-success text-success-content rounded-full w-8 h-8">
<span className="text-xs font-bold"> <span className="text-xs font-bold">
{strategy.strategyName?.charAt(0) || 'S'} {strategy.strategyName?.charAt(0) || 'S'}
</span> </span>
</div> </div>
</div> </div>
<div> <div className="min-w-0 flex-1">
<div className="text-sm text-base-content font-medium"> <div className="text-xs md:text-sm text-base-content font-medium truncate">
{strategy.strategyName || '[Strategy Name]'} {strategy.strategyName || '[Strategy Name]'}
</div> </div>
<div className="text-xs text-base-content/60"> <div className="text-[10px] md:text-xs text-base-content/60 truncate">
Vol: {formatCurrency(strategy.volume || 0)} Vol: {formatCurrency(strategy.volume || 0)}
</div> </div>
</div> </div>
</div> </div>
<div className="text-right"> <div className="text-right flex-shrink-0">
<div <div
className={`text-sm font-bold ${(strategy.roi || 0) >= 0 ? 'text-success' : 'text-error'}`}> className={`text-xs md:text-sm font-bold ${(strategy.roi || 0) >= 0 ? 'text-success' : 'text-error'}`}>
{(strategy.roi || 0) >= 0 ? '+' : ''}{strategy.roi?.toFixed(2) || 0}% {(strategy.roi || 0) >= 0 ? '+' : ''}{strategy.roi?.toFixed(2) || 0}%
</div> </div>
<div <div
className={`text-xs ${(strategy.netPnL || 0) >= 0 ? 'text-success' : 'text-error'}`}> className={`text-[10px] md:text-xs ${(strategy.netPnL || 0) >= 0 ? 'text-success' : 'text-error'}`}>
{(strategy.netPnL || 0) >= 0 ? '+' : ''}{formatCurrency(strategy.netPnL || 0)} {(strategy.netPnL || 0) >= 0 ? '+' : ''}{formatCurrency(strategy.netPnL || 0)}
</div> </div>
</div> </div>
</div> </div>
)) || ( )) || (
<div className="text-base-content/60 text-sm">No ROI data available</div> <div className="text-base-content/60 text-xs md:text-sm">No ROI data available</div>
)} )}
</div> </div>
</div> </div>
@@ -271,43 +271,43 @@ function PlatformSummary({index}: { index: number }) {
{/* Top 3 Agents by PnL */} {/* Top 3 Agents by PnL */}
<div className="card bg-base-200"> <div className="card bg-base-200">
<div className="card-body"> <div className="card-body p-3 md:p-6">
<div className="flex items-center gap-2 mb-4"> <div className="flex items-center gap-2 mb-3 md:mb-4">
<span className="text-2xl">👥</span> <span className="text-xl md:text-2xl">👥</span>
<h3 className="card-title text-base-content/70">Top 3 Agents by PnL</h3> <h3 className="card-title text-base-content/70 text-sm md:text-base">Top 3 Agents by PnL</h3>
</div> </div>
<div className="space-y-3"> <div className="space-y-2 md:space-y-3">
{topAgentsByPnL?.slice(0, 3).map((agent, index) => ( {topAgentsByPnL?.slice(0, 3).map((agent, index) => (
<div key={index} className="flex items-center justify-between"> <div key={index} className="flex items-center justify-between gap-1 md:gap-2">
<div className="flex items-center gap-3"> <div className="flex items-center gap-1.5 md:gap-3 min-w-0 flex-1">
<div className="avatar placeholder"> <div className="avatar placeholder flex-shrink-0 hidden md:flex">
<div className="bg-secondary text-secondary-content rounded-full w-8"> <div className="bg-secondary text-secondary-content rounded-full w-8 h-8">
<span className="text-xs font-bold"> <span className="text-xs font-bold">
{agent.agentName?.charAt(0) || 'A'} {agent.agentName?.charAt(0) || 'A'}
</span> </span>
</div> </div>
</div> </div>
<div> <div className="min-w-0 flex-1">
<div className="text-sm text-base-content font-medium"> <div className="text-xs md:text-sm text-base-content font-medium truncate">
{agent.agentName || '[Agent Name]'} {agent.agentName || '[Agent Name]'}
</div> </div>
<div className="text-xs text-base-content/60"> <div className="text-[10px] md:text-xs text-base-content/60 truncate">
{agent.activeStrategiesCount || 0} strategies {agent.activeStrategiesCount || 0} strategies
</div> </div>
</div> </div>
</div> </div>
<div className="text-right"> <div className="text-right flex-shrink-0">
<div <div
className={`text-sm font-bold ${(agent.netPnL || 0) >= 0 ? 'text-success' : 'text-error'}`}> className={`text-xs md:text-sm font-bold ${(agent.netPnL || 0) >= 0 ? 'text-success' : 'text-error'}`}>
{(agent.netPnL || 0) >= 0 ? '+' : ''}{formatCurrency(agent.netPnL || 0)} {(agent.netPnL || 0) >= 0 ? '+' : ''}{formatCurrency(agent.netPnL || 0)}
</div> </div>
<div className="text-xs text-base-content/60"> <div className="text-[10px] md:text-xs text-base-content/60">
{(agent.totalROI || 0).toFixed(2)}% ROI {(agent.totalROI || 0).toFixed(2)}% ROI
</div> </div>
</div> </div>
</div> </div>
)) || ( )) || (
<div className="text-base-content/60 text-sm">No agent data available</div> <div className="text-base-content/60 text-xs md:text-sm">No agent data available</div>
)} )}
</div> </div>
</div> </div>

View File

@@ -1,7 +1,6 @@
import React from 'react' import React from 'react'
import {useQuery} from '@tanstack/react-query' import {useQuery} from '@tanstack/react-query'
import useApiUrlStore from '../../../app/store/apiStore' import useApiUrlStore from '../../../app/store/apiStore'
import {Table} from '../../../components/mollecules'
// Define health check response interface based on the provided example // Define health check response interface based on the provided example
interface HealthCheckEntry { interface HealthCheckEntry {
@@ -48,6 +47,16 @@ const HealthChecks: React.FC = () => {
} }
}) })
// Use TanStack Query for API candles health check
const {data: candlesHealth, isLoading: isLoadingCandles} = useQuery({
queryKey: ['health', 'api-candles'],
queryFn: async () => {
const response = await fetch(`${apiUrl}/health-candles`)
if (!response.ok) throw new Error('Failed to fetch API candles health')
return response.json() as Promise<HealthCheckResponse>
}
})
// Use TanStack Query for Worker health check // Use TanStack Query for Worker health check
const {data: workerHealth, isLoading: isLoadingWorker} = useQuery({ const {data: workerHealth, isLoading: isLoadingWorker} = useQuery({
queryKey: ['health', 'worker'], queryKey: ['health', 'worker'],
@@ -59,9 +68,6 @@ const HealthChecks: React.FC = () => {
}) })
// Determine overall loading state
const isLoading = isLoadingApi || isLoadingWorker
// Helper function to prepare table data from health response // Helper function to prepare table data from health response
const prepareHealthData = ( const prepareHealthData = (
service: string, service: string,
@@ -85,7 +91,60 @@ const HealthChecks: React.FC = () => {
const results: any[] = []; const results: any[] = [];
Object.entries(health.entries).forEach(([key, entry]) => { Object.entries(health.entries).forEach(([key, entry]) => {
// Basic health check entry // Special handling for candle-data to expand timeframe checks
if (key === 'candle-data' && entry.data) {
// Extract timeframe checks - show all ticker-timeframe combinations
Object.entries(entry.data)
.filter(([dataKey]) => dataKey.startsWith('TimeframeCheck_'))
.forEach(([dataKey, timeframeData]) => {
const tfData = timeframeData as CandleTimeframeCheck;
// Create a more descriptive component name with ticker and timeframe
const componentName = `${tfData.CheckedTicker || 'N/A'} - ${tfData.CheckedTimeframe || 'N/A'}`
results.push({
service,
component: componentName,
status: tfData.Status,
duration: '',
tags: 'candles',
description: tfData.Message || '',
details: {
Ticker: tfData.CheckedTicker,
LatestCandle: tfData.LatestCandleDate,
TimeDifference: tfData.TimeDifference,
},
});
});
// Also check for ticker results if they exist (from detailed health check)
Object.entries(entry.data)
.filter(([dataKey]) => dataKey.startsWith('TickerResults_') || dataKey === 'TickerResults')
.forEach(([dataKey, tickerData]) => {
if (tickerData && typeof tickerData === 'object') {
Object.entries(tickerData as Record<string, any>).forEach(([ticker, tickerInfo]) => {
if (tickerInfo && typeof tickerInfo === 'object' && Array.isArray((tickerInfo as any).timeframeChecks)) {
const timeframeChecks = (tickerInfo as any).timeframeChecks as CandleTimeframeCheck[];
timeframeChecks.forEach((tfData: CandleTimeframeCheck) => {
const componentName = `${ticker} - ${tfData.CheckedTimeframe || 'N/A'}`
results.push({
service,
component: componentName,
status: tfData.Status,
duration: '',
tags: 'candles',
description: tfData.Message || '',
details: {
Ticker: tfData.CheckedTicker || ticker,
LatestCandle: tfData.LatestCandleDate,
TimeDifference: tfData.TimeDifference,
},
});
});
}
});
}
});
} else {
// Basic health check entry (skip candle-data as it's expanded above)
const baseEntry = { const baseEntry = {
service, service,
component: key, component: key,
@@ -95,31 +154,7 @@ const HealthChecks: React.FC = () => {
description: entry.description || '', description: entry.description || '',
details: null, details: null,
}; };
// Add the base entry
results.push(baseEntry); results.push(baseEntry);
// Special handling for candle-data to expand timeframe checks
if (key === 'candle-data' && entry.data) {
// Extract timeframe checks
Object.entries(entry.data)
.filter(([dataKey]) => dataKey.startsWith('TimeframeCheck_'))
.forEach(([dataKey, timeframeData]) => {
const tfData = timeframeData as CandleTimeframeCheck;
results.push({
service,
component: `${key} - ${tfData.CheckedTimeframe}`,
status: tfData.Status,
duration: '',
tags: 'candles',
description: tfData.Message,
details: {
Ticker: tfData.CheckedTicker,
LatestCandle: tfData.LatestCandleDate,
TimeDifference: tfData.TimeDifference,
},
});
});
} }
// Special handling for Web3Proxy components // Special handling for Web3Proxy components
@@ -206,96 +241,112 @@ const HealthChecks: React.FC = () => {
return results; return results;
} }
// Combine all health check data for display // Prepare health data for each service independently
const healthData = [ const apiHealthData = React.useMemo(() =>
...prepareHealthData('Managing API', apiHealth || null), prepareHealthData('Managing API', apiHealth || null),
...prepareHealthData('Managing Worker', workerHealth || null), [apiHealth]
)
const candlesHealthData = React.useMemo(() =>
prepareHealthData('Managing API - Candles', candlesHealth || null),
[candlesHealth]
)
const workerHealthData = React.useMemo(() =>
prepareHealthData('Managing Worker', workerHealth || null),
[workerHealth]
)
// Create service entries with loading states
const services = [
{
name: 'Managing API',
data: apiHealthData,
isLoading: isLoadingApi,
},
{
name: 'Managing API - Candles',
data: candlesHealthData,
isLoading: isLoadingCandles,
},
{
name: 'Managing Worker',
data: workerHealthData,
isLoading: isLoadingWorker,
},
] ]
// Define columns for the table // Get overall status for a service
const columns = React.useMemo( const getServiceStatus = (items: Array<{status: string}>) => {
() => [ if (items.some(item => item.status.toLowerCase() === 'unhealthy' || item.status.toLowerCase() === 'unreachable')) {
{ return { status: 'Unhealthy', color: 'badge-error' }
Header: 'Service', }
accessor: 'service', if (items.some(item => item.status.toLowerCase() === 'degraded')) {
disableSortBy: true, return { status: 'Degraded', color: 'badge-warning' }
disableFilters: true, }
}, return { status: 'Healthy', color: 'badge-success' }
{ }
Header: 'Component',
accessor: 'component', const getStatusBadge = (status: string) => {
disableSortBy: true, const statusLower = status.toLowerCase()
disableFilters: true, if (statusLower === 'healthy') return 'badge-success badge-xs'
}, if (statusLower === 'unhealthy' || statusLower === 'unreachable') return 'badge-error badge-xs'
{ return 'badge-warning badge-xs'
Header: 'Status', }
accessor: 'status',
Cell: ({value}: { value: string }) => (
<span
className={`badge ${
value === 'Healthy' || value === 'healthy'
? 'badge-success'
: value === 'Unreachable' || value === 'unhealthy'
? 'badge-error'
: 'badge-warning'
}`}
>
{value.charAt(0).toUpperCase() + value.slice(1)}
</span>
),
disableSortBy: true,
disableFilters: true,
},
{
Header: 'Description',
accessor: 'description',
disableSortBy: true,
disableFilters: true,
Cell: ({value, row}: any) => (
<div>
<div>{value}</div>
{row.original.details && (
<div className="text-xs mt-1 opacity-80">
{Object.entries(row.original.details).filter(([_, val]) => val !== undefined).map(
([key, val]) => (
<div key={key}>
<span className="font-semibold">{key}:</span> {String(val)}
</div>
)
)}
</div>
)}
</div>
),
},
{
Header: 'Duration',
accessor: 'duration',
disableSortBy: true,
disableFilters: true,
},
{
Header: 'Tags',
accessor: 'tags',
disableSortBy: true,
disableFilters: true,
},
],
[]
)
return ( return (
<div className="container mx-auto"> <div className="container mx-auto p-1 md:p-2">
<h2 className="text-xl font-bold mb-4">System Health Status</h2> <div className="space-y-1.5">
{isLoading ? ( {services.map((service) => {
<progress className="progress progress-primary w-56"></progress> if (service.isLoading && service.data.length === 0) {
) : ( return (
<Table <div key={service.name} className="card bg-base-200">
columns={columns} <div className="card-body p-1.5 md:p-2">
data={healthData} <div className="flex items-center justify-between mb-0.5">
showPagination={true} <h3 className="text-[10px] md:text-xs font-semibold">{service.name}</h3>
/> <progress className="progress progress-primary w-16 h-0.5"></progress>
</div>
</div>
</div>
)
}
if (service.data.length === 0) {
return null
}
const serviceStatus = getServiceStatus(service.data)
return (
<div key={service.name} className="card bg-base-200">
<div className="card-body p-1.5 md:p-2">
<div className="flex items-center justify-between mb-0.5">
<h3 className="text-[10px] md:text-xs font-semibold">{service.name}</h3>
<div className="flex items-center gap-1">
{service.isLoading && (
<progress className="progress progress-primary w-8 h-0.5"></progress>
)} )}
<div className={`badge ${serviceStatus.color} badge-xs px-1 py-0 text-[8px]`}>
{serviceStatus.status}
</div>
</div>
</div>
<div className="grid grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-0.5 md:gap-1">
{service.data.map((item, idx) => (
<div key={idx} className="flex items-center justify-between bg-base-100 rounded px-1 py-0.5 text-[10px]">
<div className="flex-1 min-w-0">
<div className="truncate font-medium leading-tight">{item.component}</div>
</div>
<div className={`badge ${getStatusBadge(item.status)} ml-0.5 flex-shrink-0 px-0.5 py-0 text-[8px]`}>
{item.status.charAt(0).toUpperCase()}
</div>
</div>
))}
</div>
</div>
</div>
)
})}
</div>
</div> </div>
) )
} }

View File

@@ -265,7 +265,6 @@ const SqlMonitoring: React.FC = () => {
<div className="container mx-auto space-y-3 sm:space-y-6 px-2 sm:px-4"> <div className="container mx-auto space-y-3 sm:space-y-6 px-2 sm:px-4">
{/* Header */} {/* Header */}
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-2 sm:gap-4"> <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-2 sm:gap-4">
<h2 className="text-xl sm:text-2xl font-bold">SQL Monitoring</h2>
<button <button
className="btn btn-outline btn-xs sm:btn-sm" className="btn btn-outline btn-xs sm:btn-sm"
onClick={() => clearTrackingMutation.mutate()} onClick={() => clearTrackingMutation.mutate()}

View File

@@ -72,27 +72,27 @@ const WhitelistSettings: React.FC = () => {
Search Filters Search Filters
</div> </div>
<div className="collapse-content"> <div className="collapse-content">
<div className="flex gap-4 pt-4"> <div className="flex gap-2 pt-2">
<div className="form-control w-full max-w-xs"> <div className="form-control w-full max-w-[200px]">
<label className="label"> <label className="label py-1">
<span className="label-text">Search by Ethereum Account</span> <span className="label-text text-xs">Search by Ethereum Account</span>
</label> </label>
<input <input
type="text" type="text"
placeholder="Enter Ethereum address..." placeholder="Enter Ethereum address..."
className="input input-bordered w-full" className="input input-bordered input-sm w-full"
value={searchExternalEthereumAccount} value={searchExternalEthereumAccount}
onChange={(e) => handleSearchExternalEthereum(e.target.value)} onChange={(e) => handleSearchExternalEthereum(e.target.value)}
/> />
</div> </div>
<div className="form-control w-full max-w-xs"> <div className="form-control w-full max-w-[200px]">
<label className="label"> <label className="label py-1">
<span className="label-text">Search by Twitter Account</span> <span className="label-text text-xs">Search by Twitter Account</span>
</label> </label>
<input <input
type="text" type="text"
placeholder="Enter Twitter handle..." placeholder="Enter Twitter handle..."
className="input input-bordered w-full" className="input input-bordered input-sm w-full"
value={searchTwitterAccount} value={searchTwitterAccount}
onChange={(e) => handleSearchTwitter(e.target.value)} onChange={(e) => handleSearchTwitter(e.target.value)}
/> />

View File

@@ -9,19 +9,19 @@ import FeeCalculator from './feeCalculator'
const tabs: ITabsType = [ const tabs: ITabsType = [
{ {
Component: SpotlightView, Component: FeeCalculator,
index: 1, index: 1,
label: 'Fee Calculator',
},
{
Component: SpotlightView,
index: 2,
label: 'Spotlight', label: 'Spotlight',
}, },
{ {
Component: RektFees, Component: RektFees,
index: 2,
label: 'RektFees',
},
{
Component: FeeCalculator,
index: 3, index: 3,
label: 'Fee Calculator', label: 'RektFees',
}, },
] ]

View File

@@ -26,6 +26,15 @@
.layout { .layout {
@apply sm:w-11/12 w-10/12 mx-auto; @apply sm:w-11/12 w-10/12 mx-auto;
} }
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
} }
@layer components { @layer components {