import { useMemo, useState, useEffect } from 'react' import { Box, Tabs, Tab, Typography, useTheme } from '@mui/material' import { useI18nStore } from '../../stores/i18n.store' import { useRedisStateStore } from '../../stores/redis-state.store' import { useThemeStore } from '../../stores/theme.store' import { useMainCommandStore, onCommandEvent } from '../../stores/main-command.store' const EXCLUDE = ['in', 'run', 'per'] const INCLUDE = ['sha1'] const REPLACE: Record = { perc: 'percent', sec: 'seconds' } function generateKey(key: string, strings: any): string { if (strings?.title?.hasOwnProperty(key)) { return strings.title[key] } return key.split('_').map((instance, index) => { if (REPLACE.hasOwnProperty(instance)) { instance = REPLACE[instance] } if (INCLUDE.includes(instance) || (instance.length < 4 && !EXCLUDE.includes(instance))) { return instance.toUpperCase() } else if (index === 0) { return instance[0].toUpperCase() + instance.substring(1) } return instance }).join(' ') } function formatValue(value: any): string { if (value === null || value === undefined) return '' if (typeof value === 'object') return JSON.stringify(value) return String(value) } interface TabSection { key: string label: string items: Array<{ key: string; value: any }> } export default function StatisticsPage() { const strings = useI18nStore(s => s.strings) const info = useRedisStateStore(s => s.info) const modules = useRedisStateStore(s => s.modules) const connection = useRedisStateStore(s => s.connection) const redisChanged = useRedisStateStore(s => s.redisChanged) const muiTheme = useTheme() const themeKey = useThemeStore(s => s.themeKey) const [topTab, setTopTab] = useState(0) const [dbTab, setDbTab] = useState(0) const itemBorderColor = muiTheme.palette.mode === 'dark' ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)' // Broadcast tree refresh if state changed useEffect(() => { if (redisChanged) { useRedisStateStore.setState({ redisChanged: false }) } }, [redisChanged]) const isCluster = connection?.cluster === true // Parse keyspace databases const keyspaceDatabaseEntries = useMemo(() => { const ksDbs = info?.keyspaceDatabases ?? {} return Object.keys(ksDbs).map(k => ({ key: k, value: ksDbs[k] })) }, [info]) const keyspaceItems = useMemo(() => { const result: Record> = {} for (const dbEntry of keyspaceDatabaseEntries) { const ks = info?.keyspace?.['db' + dbEntry.key] result[dbEntry.key] = ks ? Object.keys(ks).map(k => ({ key: k, value: ks[k] })) : [] } return result }, [info, keyspaceDatabaseEntries]) const hasDatabases = keyspaceDatabaseEntries.length > 0 // Parse info sections + modules const infoSections: TabSection[] = useMemo(() => { if (!info) return [] const sections = Object.keys(info) .filter(k => k !== 'keyspace' && k !== 'keyspaceDatabases') .map(k => ({ key: k, label: generateKey(k, strings), items: Object.keys(info[k]).map(ik => ({ key: ik, value: info[k][ik] })), })) // Replace or add modules section const mods = Array.isArray(modules) ? modules : [] if (mods.length > 0) { const moduleItems = mods.map((m: any) => ({ key: m.name, value: `v${m.ver}` })) const existingIdx = sections.findIndex(s => s.key.toLowerCase() === 'modules') if (existingIdx >= 0) { sections[existingIdx].items = moduleItems } else { sections.push({ key: 'modules', label: generateKey('modules', strings), items: moduleItems }) } } return sections }, [info, modules, strings]) // All top-level tabs: DB (if applicable) + info sections const allTabs = useMemo(() => { const tabs: TabSection[] = [] if (hasDatabases && !isCluster) { tabs.push({ key: '__db__', label: strings?.title?.db ?? 'DB', items: [], }) } tabs.push(...infoSections) return tabs }, [hasDatabases, isCluster, infoSections, strings]) if (!connection) { return {strings?.title?.main} } const currentTab = allTabs[topTab] const isDbTab = currentTab?.key === '__db__' return ( {/* Top-level tabs */} setTopTab(v)} variant="scrollable" scrollButtons="auto" textColor="inherit" sx={{ position: 'sticky', top: 0, zIndex: 2, bgcolor: muiTheme.palette.background.paper, backgroundImage: 'none', flexShrink: 0, '& .MuiTab-root': { color: muiTheme.p3xr.treecontrolIconColor, textTransform: 'none' }, '& .MuiTab-root.Mui-selected': { color: muiTheme.palette.text.primary }, '& .MuiTabs-indicator': { bgcolor: muiTheme.p3xr.matSysPrimary }, }} > {allTabs.map(tab => ( ))} {/* Tab content */} {isDbTab ? ( /* Nested DB keyspace tabs */ setDbTab(v)} variant="scrollable" scrollButtons="auto" textColor="inherit" sx={() => { const isMatrix = themeKey === 'matrix' const activeColor = isMatrix ? 'rgba(0,0,0,0.87)' : 'white' const inactiveColor = isMatrix ? 'rgba(0,0,0,0.6)' : 'rgba(255,255,255,0.7)' return { bgcolor: 'primary.main', '& .MuiTab-root': { color: inactiveColor }, '& .MuiTab-root.Mui-selected': { color: activeColor }, '& .MuiTabs-indicator': { bgcolor: activeColor }, '& .MuiTabScrollButton-root': { color: inactiveColor }, } }} > {keyspaceDatabaseEntries.map(dbEntry => ( ))} {keyspaceDatabaseEntries.map((dbEntry, i) => ( dbTab === i && ( {(keyspaceItems[dbEntry.key] ?? []).map(item => ( {generateKey(item.key, strings)} {item.value} ))} ) ))} ) : currentTab ? ( /* Info section content */ {currentTab.items.map(item => ( {generateKey(item.key, strings)} {formatValue(item.value)} ))} ) : null} ) }