Commit 8a720078 by t0ng7u

šŸ“± feat(ui): Enhance mobile log table UX & fix StrictMode warning

Summary
1. CardTable
   • Added collapsible ā€œDetails / Collapseā€ section on mobile cards using Semi-UI Button + Collapsible with chevron icons.
   • Integrated i18n (`useTranslation`) for the toggle labels.
   • Restored original variable-width skeleton placeholders (50 % / 60 % / 70 % …) for more natural loading states.

2. UsageLogsColumnDefs
   • Wrapped each `Tag` inside a native `<span>` when used as Tooltip trigger, removing `findDOMNode` deprecation warnings in React StrictMode.

Impact
• Cleaner, shorter rows on small screens with optional expansion.
• Fully translated UI controls.
• No more console noise in development & CI caused by StrictMode warnings.
parent 03f594d2
......@@ -18,7 +18,9 @@ For commercial licensing, please contact support@quantumnous.com
*/
import React, { useState, useEffect, useRef } from 'react';
import { Table, Card, Skeleton, Pagination, Empty } from '@douyinfe/semi-ui';
import { useTranslation } from 'react-i18next';
import { Table, Card, Skeleton, Pagination, Empty, Button, Collapsible } from '@douyinfe/semi-ui';
import { IconChevronDown, IconChevronUp } from '@douyinfe/semi-icons';
import PropTypes from 'prop-types';
import { useIsMobile } from '../../../hooks/common/useIsMobile';
......@@ -30,6 +32,7 @@ import { useIsMobile } from '../../../hooks/common/useIsMobile';
*/
const CardTable = ({ columns = [], dataSource = [], loading = false, rowKey = 'key', ...tableProps }) => {
const isMobile = useIsMobile();
const { t } = useTranslation();
// Skeleton ę˜¾ē¤ŗęŽ§åˆ¶ļ¼Œē”®äæč‡³å°‘å±•ē¤ŗ 500ms åŠØę•ˆ
const [showSkeleton, setShowSkeleton] = useState(loading);
......@@ -94,7 +97,14 @@ const CardTable = ({ columns = [], dataSource = [], loading = false, rowKey = 'k
return (
<div key={idx} className="flex justify-between items-center py-1 border-b last:border-b-0 border-dashed" style={{ borderColor: 'var(--semi-color-border)' }}>
<Skeleton.Title active style={{ width: 80, height: 14 }} />
<Skeleton.Title active style={{ width: `${50 + (idx % 3) * 10}%`, maxWidth: 180, height: 14 }} />
<Skeleton.Title
active
style={{
width: `${50 + (idx % 3) * 10}%`,
maxWidth: 180,
height: 14,
}}
/>
</div>
);
})}
......@@ -118,6 +128,78 @@ const CardTable = ({ columns = [], dataSource = [], loading = false, rowKey = 'k
// ęø²ęŸ“ē§»åŠØē«Æå”ē‰‡
const isEmpty = !showSkeleton && (!dataSource || dataSource.length === 0);
// ē§»åŠØē«Æč”Œå”ē‰‡ē»„ä»¶ļ¼ˆå«åÆęŠ˜å čÆ¦ęƒ…ļ¼‰
const MobileRowCard = ({ record, index }) => {
const [showDetails, setShowDetails] = useState(false);
const rowKeyVal = getRowKey(record, index);
const hasDetails =
tableProps.expandedRowRender &&
(!tableProps.rowExpandable || tableProps.rowExpandable(record));
return (
<Card key={rowKeyVal} className="!rounded-2xl shadow-sm">
{columns.map((col, colIdx) => {
// åæ½ē•„éšč—åˆ—
if (tableProps?.visibleColumns && !tableProps.visibleColumns[col.key]) {
return null;
}
const title = col.title;
const cellContent = col.render
? col.render(record[col.dataIndex], record, index)
: record[col.dataIndex];
// ē©ŗę ‡é¢˜åˆ—ļ¼ˆé€šåøøäøŗę“ä½œęŒ‰é’®ļ¼‰å•ē‹¬ęø²ęŸ“
if (!title) {
return (
<div key={col.key || colIdx} className="mt-2 flex justify-end">
{cellContent}
</div>
);
}
return (
<div
key={col.key || colIdx}
className="flex justify-between items-start py-1 border-b last:border-b-0 border-dashed"
style={{ borderColor: 'var(--semi-color-border)' }}
>
<span className="font-medium text-gray-600 mr-2 whitespace-nowrap select-none">
{title}
</span>
<div className="flex-1 break-all flex justify-end items-center gap-1">
{cellContent !== undefined && cellContent !== null ? cellContent : '-'}
</div>
</div>
);
})}
{hasDetails && (
<>
<Button
theme='borderless'
size='small'
className='w-full flex justify-center mt-2'
icon={showDetails ? <IconChevronUp /> : <IconChevronDown />}
onClick={(e) => {
e.stopPropagation();
setShowDetails(!showDetails);
}}
>
{showDetails ? t('ę”¶čµ·') : t('čÆ¦ęƒ…')}
</Button>
<Collapsible isOpen={showDetails} keepDOM>
<div className="pt-2">
{tableProps.expandedRowRender(record, index)}
</div>
</Collapsible>
</>
)}
</Card>
);
};
if (isEmpty) {
// č‹„ä¼ å…„ empty å±žę€§åˆ™ä½æē”Øä¹‹ļ¼Œå¦åˆ™ä½æē”Øé»˜č®¤ Empty
if (tableProps.empty) return tableProps.empty;
......@@ -130,52 +212,9 @@ const CardTable = ({ columns = [], dataSource = [], loading = false, rowKey = 'k
return (
<div className="flex flex-col gap-2">
{dataSource.map((record, index) => {
const rowKeyVal = getRowKey(record, index);
return (
<Card key={rowKeyVal} className="!rounded-2xl shadow-sm">
{columns.map((col, colIdx) => {
// åæ½ē•„éšč—åˆ—
if (tableProps?.visibleColumns && !tableProps.visibleColumns[col.key]) {
return null;
}
const title = col.title;
// č®”ē®—å•å…ƒę ¼å†…å®¹
const cellContent = col.render
? col.render(record[col.dataIndex], record, index)
: record[col.dataIndex];
// ē©ŗę ‡é¢˜åˆ—ļ¼ˆé€šåøøäøŗę“ä½œęŒ‰é’®ļ¼‰å•ē‹¬ęø²ęŸ“
if (!title) {
return (
<div
key={col.key || colIdx}
className="mt-2 flex justify-end"
>
{cellContent}
</div>
);
}
return (
<div
key={col.key || colIdx}
className="flex justify-between items-start py-1 border-b last:border-b-0 border-dashed"
style={{ borderColor: 'var(--semi-color-border)' }}
>
<span className="font-medium text-gray-600 mr-2 whitespace-nowrap select-none">
{title}
</span>
<div className="flex-1 break-all flex justify-end items-center gap-1">
{cellContent !== undefined && cellContent !== null ? cellContent : '-'}
</div>
</div>
);
})}
</Card>
);
})}
{dataSource.map((record, index) => (
<MobileRowCard key={getRowKey(record, index)} record={record} index={index} />
))}
{/* åˆ†é”µē»„ä»¶ */}
{tableProps.pagination && dataSource.length > 0 && (
<div className="mt-2 flex justify-center">
......
......@@ -268,12 +268,14 @@ export const getLogsColumns = ({
return isAdminUser && (record.type === 0 || record.type === 2 || record.type === 5) ? (
<Space>
<Tooltip content={record.channel_name || t('ęœŖēŸ„ęø é“')}>
<Tag
color={colors[parseInt(text) % colors.length]}
shape='circle'
>
{text}
</Tag>
<span>
<Tag
color={colors[parseInt(text) % colors.length]}
shape='circle'
>
{text}
</Tag>
</span>
</Tooltip>
{isMultiKey && (
<Tag color='white' shape='circle'>
......@@ -466,15 +468,17 @@ export const getLogsColumns = ({
render: (text, record, index) => {
return (record.type === 2 || record.type === 5) && text ? (
<Tooltip content={text}>
<Tag
color='orange'
shape='circle'
onClick={(event) => {
copyText(event, text);
}}
>
{text}
</Tag>
<span>
<Tag
color='orange'
shape='circle'
onClick={(event) => {
copyText(event, text);
}}
>
{text}
</Tag>
</span>
</Tooltip>
) : (
<></>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment