Skip to content

Commit a11c49f

Browse files
authored
Add duplicate file replacement (#19)
* feat: replace existing file data when uploading duplicates * feat(ui): sync x-axis to shortest enabled log
1 parent 5e8d200 commit a11c49f

File tree

6 files changed

+77
-1
lines changed

6 files changed

+77
-1
lines changed

src/App.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ComparisonControls } from './components/ComparisonControls';
77
import { Header } from './components/Header';
88
import { FileConfigModal } from './components/FileConfigModal';
99
import { PanelLeftClose, PanelLeftOpen } from 'lucide-react';
10+
import { mergeFilesWithReplacement } from './utils/mergeFiles.js';
1011

1112
function App() {
1213
const [uploadedFiles, setUploadedFiles] = useState([]);
@@ -54,7 +55,7 @@ function App() {
5455
}
5556
}
5657
}));
57-
setUploadedFiles(prev => [...prev, ...filesWithDefaults]);
58+
setUploadedFiles(prev => mergeFilesWithReplacement(prev, filesWithDefaults));
5859
}, [globalParsingConfig]);
5960

6061
// 全局文件处理函数

src/components/ChartContainer.jsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
Legend,
1414
} from 'chart.js';
1515
import zoomPlugin from 'chartjs-plugin-zoom';
16+
import { getMinSteps } from "../utils/getMinSteps.js";
1617

1718
ChartJS.register(
1819
CategoryScale,
@@ -164,6 +165,17 @@ export default function ChartContainer({
164165
onMaxStepChange(maxStep);
165166
}, [parsedData, onMaxStepChange]);
166167

168+
useEffect(() => {
169+
const minSteps = getMinSteps(parsedData);
170+
if (minSteps > 0) {
171+
onXRangeChange(prev => {
172+
const next = { min: 0, max: minSteps - 1 };
173+
if (prev.min === next.min && prev.max === next.max) return prev;
174+
return next;
175+
});
176+
}
177+
}, [parsedData, onXRangeChange]);
178+
167179
const colors = ['#ef4444', '#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#f97316'];
168180
const createChartData = dataArray => ({
169181
datasets: dataArray.map((item, index) => {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { getMinSteps } from '../getMinSteps.js';
3+
4+
describe('getMinSteps', () => {
5+
it('returns minimum length among enabled files', () => {
6+
const parsed = [
7+
{ enabled: true, metricsData: { a: [{},{},{}], b: [{},{}] } },
8+
{ enabled: true, metricsData: { a: [{},{},{} ,{}], b: [{},{} ,{}] } },
9+
];
10+
expect(getMinSteps(parsed)).toBe(3);
11+
});
12+
13+
it('ignores disabled files', () => {
14+
const parsed = [
15+
{ enabled: false, metricsData: { a: [{},{},{}] } },
16+
{ enabled: true, metricsData: { a: [{},{}] } }
17+
];
18+
expect(getMinSteps(parsed)).toBe(2);
19+
});
20+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { mergeFilesWithReplacement } from '../mergeFiles.js';
3+
4+
describe('mergeFilesWithReplacement', () => {
5+
it('replaces file with same name and keeps config', () => {
6+
const prev = [{ name: 'log1.txt', id: '1', enabled: true, config: { a: 1 }, content: 'old' }];
7+
const newFile = { name: 'log1.txt', id: '2', enabled: true, config: {}, content: 'new' };
8+
const result = mergeFilesWithReplacement(prev, [newFile]);
9+
expect(result).toHaveLength(1);
10+
expect(result[0].content).toBe('new');
11+
expect(result[0].config).toEqual({ a: 1 });
12+
});
13+
14+
it('adds new file when name does not exist', () => {
15+
const prev = [{ name: 'log1.txt', id: '1', enabled: true, config: {}, content: 'old' }];
16+
const newFile = { name: 'log2.txt', id: '2', enabled: true, config: {}, content: 'new' };
17+
const result = mergeFilesWithReplacement(prev, [newFile]);
18+
expect(result).toHaveLength(2);
19+
});
20+
});

src/utils/getMinSteps.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export function getMinSteps(parsedFiles = []) {
2+
const enabled = parsedFiles.filter(f => f.enabled !== false);
3+
if (enabled.length === 0) return 0;
4+
const lengths = enabled.map(file => {
5+
const datasets = Object.values(file.metricsData || {});
6+
if (datasets.length === 0) return 0;
7+
return datasets.reduce((m, d) => Math.max(m, d.length), 0);
8+
});
9+
return Math.min(...lengths);
10+
}

src/utils/mergeFiles.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export function mergeFilesWithReplacement(prevFiles, newFiles) {
2+
const updated = [...prevFiles];
3+
newFiles.forEach(file => {
4+
const idx = updated.findIndex(f => f.name === file.name);
5+
if (idx !== -1) {
6+
const existing = updated[idx];
7+
updated[idx] = { ...file, enabled: existing.enabled, config: existing.config };
8+
} else {
9+
updated.push(file);
10+
}
11+
});
12+
return updated;
13+
}

0 commit comments

Comments
 (0)