/** * Sorted Set key type renderer — exact port of Angular key-zset.component. * Table with score + member. Flat array [member, score, member, score, ...]. * Uses zsetMode paging (pairs). */ import { useState, useEffect, useCallback } from 'react' import { Box, Tooltip } from '@mui/material' import { Delete, TableChart, ContentCopy, Download, Edit, Add } from '@mui/icons-material' import { useTheme } from '@mui/material' import { useI18nStore } from '../../../stores/i18n.store' import { useRedisStateStore } from '../../../stores/redis-state.store' import { useCommonStore } from '../../../stores/common.store' import { request } from '../../../stores/socket.service' import { KeyTypeProps, createPaging, Paging, pagerAction, pageChange, formatValue, truncateDisplay, isTruncated, copyValue, downloadBuffer } from './key-type-base' import KeyPagerInline from './KeyPagerInline' import KeyNewOrSetDialog from '../../../dialogs/KeyNewOrSetDialog' import JsonViewDialog from '../../../dialogs/JsonViewDialog' interface ZsetItem { score: string; member: string; index: number } function buildItems(value: any[], paging: Paging): ZsetItem[] { if (!value) return [] const items: ZsetItem[] = [] for (let i = 0; i < value.length; i += 2) { items.push({ member: value[i], score: value[i + 1], index: i / 2 }) } return items.slice(paging.startIndex, paging.endIndex) } export default function KeyZset({ response, value, valueBuffer, keyName, valueFormat, onRefresh }: KeyTypeProps) { const strings = useI18nStore(s => s.strings) const connection = useRedisStateStore(s => s.connection) const { toast, confirm, generalHandleError } = useCommonStore() const muiTheme = useTheme() const isReadonly = connection?.readonly === true const isDark = muiTheme.palette.mode === 'dark' const [paging, setPaging] = useState(() => createPaging(value?.length ?? 0, true)) const [pagedItems, setPagedItems] = useState([]) const [editDialogOpen, setEditDialogOpen] = useState(false) const [editDialogData, setEditDialogData] = useState(null) const [jsonViewOpen, setJsonViewOpen] = useState(false) const [jsonViewValue, setJsonViewValue] = useState('') useEffect(() => { if (!value) return const p = createPaging(value.length, true) setPaging(p) setPagedItems(buildItems(value, p)) }, [value]) const updatePagedItems = useCallback((p: Paging) => { setPaging(p) setPagedItems(buildItems(value, p)) }, [value]) const addZSet = useCallback(() => { setEditDialogData({ type: 'append', model: { type: 'zset', key: keyName } }) setEditDialogOpen(true) }, [keyName]) const deleteZSet = useCallback(async (item: ZsetItem) => { try { await confirm({ message: strings?.confirm?.deleteZSetMember ?? strings?.confirm?.areYouSure ?? 'Are you sure?' }) await request({ action: 'key-zset-delete-member', payload: { key: keyName, value: valueBuffer?.[item.index * 2] } }) toast(strings?.status?.deletedZSetMember) onRefresh() } catch (e) { generalHandleError(e) } }, [keyName, valueBuffer, strings, confirm, toast, onRefresh, generalHandleError]) const editValue = useCallback((item: ZsetItem) => { const editVal = typeof item.member === 'string' && item.member.length >= (useRedisStateStore.getState() as any).maxValueAsBuffer ? valueBuffer?.[item.index * 2] : item.member setEditDialogData({ type: 'edit', model: { type: 'zset', key: keyName, value: editVal, score: item.score } }) setEditDialogOpen(true) }, [keyName, valueBuffer]) const handleEditClose = useCallback((result?: any) => { setEditDialogOpen(false) setEditDialogData(null) if (result) onRefresh() }, [onRefresh]) const hoverBg = isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)' const oddBg = isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.04)' const listBorder = isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.06)' const iconSx = (color: string) => ({ fontSize: 18, cursor: 'pointer', mx: '2px', opacity: 0.7, color, '&:hover': { opacity: 1 } }) return ( {strings?.page?.key?.zset?.table?.score ?? 'Score'} {strings?.page?.key?.zset?.table?.value ?? 'Member'} {!isReadonly && ( )} {pagedItems.map((item, i) => ( editValue(item)}>{item.score} editValue(item)}> {formatValue(truncateDisplay(item.member), valueFormat)} {isTruncated(item.member) && ...} {!isReadonly && deleteZSet(item)} />} { setJsonViewValue(String(item.member ?? '')); setJsonViewOpen(true) }} /> copyValue(item.member)} /> downloadBuffer(valueBuffer?.[item.index * 2], keyName)} /> {!isReadonly && editValue(item)} />} ))} setJsonViewOpen(false)} /> ) }