Skip to content

Commit a091724

Browse files
committed
feat: Add file specifications display and fix total visits mode
- Add file info display with formatted titles for Astra-generated files - Show file source (Astra App, File Upload, URL Parameter) with appropriate icons - Fix total visits mode: arrows now properly reflect total visit counts - Fix maximum minimum threshold calculation to use appropriate data source - Update menu text to be dynamic based on mode (Students vs Visits) - Enhance export button with download icon and improved styling - Fix filtered graph alignment by standardizing header margins - Remove quick workflow box from upload interface - Add comprehensive connectivity checking for selected sequence 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent fe70a4e commit a091724

File tree

5 files changed

+194
-43
lines changed

5 files changed

+194
-43
lines changed

src/App.tsx

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,54 @@ import Loading from './components/Loading.tsx';
1818
import Switch from "./components/switch.tsx";
1919
import { useSearchParams } from 'react-router-dom';
2020

21+
// Helper function to parse and format filename for display
22+
const formatFileTitle = (filename: string): string => {
23+
// Remove file extension
24+
const nameWithoutExt = filename.replace(/\.(csv|CSV)$/, '');
25+
26+
// Split by hyphens and process each part
27+
const parts = nameWithoutExt.split('-').map(part => {
28+
// Handle specific abbreviations and terms
29+
switch (part.toLowerCase()) {
30+
case 'er':
31+
return 'Equivalent Ratios';
32+
case 'me':
33+
return 'Means & Extremes';
34+
case 'groundtruth':
35+
case 'ground_truth':
36+
return 'Ground Truth';
37+
case 'successful':
38+
return 'Successful';
39+
case 'unsuccessful':
40+
return 'Unsuccessful';
41+
case 'strategies':
42+
return 'Strategies';
43+
case 'match':
44+
return 'Match';
45+
case 'allstrategies':
46+
case 'all_strategies':
47+
return 'All Strategies';
48+
case 'astra':
49+
return 'ASTRA Generated';
50+
default:
51+
// Capitalize first letter of each word
52+
return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
53+
}
54+
});
55+
56+
return parts.join(' ');
57+
};
58+
59+
// Helper function to get file type icon
60+
const getFileTypeIcon = (filename: string): string => {
61+
if (filename.includes('astra')) return '🤖'; // AI/Astra generated
62+
if (filename.includes('successful')) return '✅'; // Successful strategies
63+
if (filename.includes('unsuccessful')) return '❌'; // Unsuccessful strategies
64+
if (filename.includes('ER')) return '🔢'; // Equivalent Ratios
65+
if (filename.includes('ME')) return '✖️'; // Means & Extremes
66+
return '📄'; // Default file icon
67+
};
68+
2169
function App() {
2270
// State to hold the uploaded CSV data as a string
2371
// const [csvData, setCsvData] = useState<string>('');
@@ -27,6 +75,7 @@ function App() {
2775
const [selfLoops, setSelfLoops] = useState<boolean>(true);
2876
const [errorMode, setErrorMode] = useState<boolean>(false);
2977
const [uniqueStudentMode, setUniqueStudentMode] = useState<boolean>(false);
78+
const [fileInfo, setFileInfo] = useState<{filename: string, source: string} | null>(null);
3079
// State to manage the minimum number of visits for displaying edges in the graph
3180
const [minVisitsPercentage, setMinVisitsPercentage] = useState<number>(0);
3281
const {
@@ -64,6 +113,10 @@ function App() {
64113
// Only load from URL if no CSV data is currently loaded
65114
if (csvData.length === 0) {
66115
if (csvUrl) {
116+
// Extract filename from URL
117+
const filename = csvUrl.split('/').pop() || 'Unknown File';
118+
setFileInfo({ filename, source: 'Astra App' });
119+
67120
// Fetch CSV from URL
68121
fetch(csvUrl)
69122
.then(response => {
@@ -77,19 +130,29 @@ function App() {
77130
})
78131
.catch(error => {
79132
console.error('Error fetching CSV from URL:', error);
133+
setFileInfo(null); // Clear file info on error
80134
});
81135
} else if (csvDataParam) {
82136
// Use CSV data directly from URL parameter
83137
try {
84138
const decodedData = decodeURIComponent(csvDataParam);
139+
setFileInfo({ filename: 'URL Data', source: 'URL Parameter' });
85140
handleDataProcessed(decodedData);
86141
} catch (error) {
87142
console.error('Error decoding CSV data from URL:', error);
143+
setFileInfo(null);
88144
}
89145
}
90146
}
91147
}, [searchParams]);
92148

149+
// Clear file info when CSV data is reset
150+
useEffect(() => {
151+
if (csvData.length === 0) {
152+
setFileInfo(null);
153+
}
154+
}, [csvData]);
155+
93156
const showControls = useMemo(() => {
94157
return !loading && csvData.length > 0;
95158
}, [loading, csvData]);
@@ -128,8 +191,15 @@ function App() {
128191
* Updates the `csvData` state with the uploaded CSV data when the file is processed.
129192
*
130193
* @param {string} uploadedCsvData - The CSV data from the uploaded file.
194+
* @param {string} filename - Optional filename for display purposes.
131195
*/
132-
const handleDataProcessed = (uploadedCsvData: string) => setCSVData(uploadedCsvData);
196+
const handleDataProcessed = (uploadedCsvData: string, filename?: string) => {
197+
setCSVData(uploadedCsvData);
198+
// If filename is provided (from file upload), update file info
199+
if (filename) {
200+
setFileInfo({ filename, source: 'File Upload' });
201+
}
202+
};
133203

134204
// Calculate actual min visits from percentage
135205
const minVisits = Math.round((minVisitsPercentage / 100) * maxEdgeCount);
@@ -162,7 +232,10 @@ function App() {
162232
<div className="flex items-center space-x-4">
163233
<Button
164234
variant="destructive"
165-
onClick={resetData}
235+
onClick={() => {
236+
resetData();
237+
setFileInfo(null);
238+
}}
166239
>
167240
Reset
168241
</Button>
@@ -194,6 +267,28 @@ function App() {
194267
</h2>
195268
)}
196269
</div>
270+
271+
{/* File Information Display */}
272+
{fileInfo && (
273+
<div className="file-info-bar bg-blue-50 border border-blue-200 rounded-lg p-4 mb-4">
274+
<div className="flex items-center gap-3">
275+
<span className="text-2xl">{getFileTypeIcon(fileInfo.filename)}</span>
276+
<div className="flex-1">
277+
<h3 className="text-lg font-semibold text-blue-900">
278+
{formatFileTitle(fileInfo.filename)}
279+
</h3>
280+
<div className="flex items-center gap-4 text-sm text-blue-700 mt-1">
281+
<span className="bg-blue-100 px-2 py-1 rounded text-xs font-medium">
282+
{fileInfo.source}
283+
</span>
284+
<span className="font-mono text-xs">
285+
{fileInfo.filename}
286+
</span>
287+
</div>
288+
</div>
289+
</div>
290+
</div>
291+
)}
197292
{/* Properties Button */}
198293
<Popover>
199294
<PopoverTrigger

src/components/DataFileSelector.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ interface DataFile {
1313
}
1414

1515
interface DataFilesSelectorProps {
16-
onDataProcessed: (csvData: string) => void;
16+
onDataProcessed: (csvData: string, filename?: string) => void;
1717
}
1818

1919
function DataFileSelector({ onDataProcessed }: DataFilesSelectorProps) {
@@ -133,7 +133,7 @@ function DataFileSelector({ onDataProcessed }: DataFilesSelectorProps) {
133133
throw new Error('File is empty or invalid');
134134
}
135135

136-
onDataProcessed(csvData);
136+
onDataProcessed(csvData, selectedFile);
137137

138138
} catch (err) {
139139
setError(err instanceof Error ? err.message : 'Failed to load file');

src/components/GraphvizParent.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,10 @@ const GraphvizParent: React.FC<GraphvizParentProps> = ({
107107

108108
// Note: edgeCounts are now used directly from mainGraphData
109109

110-
// Update the maxEdgeCount in the parent component
111-
onMaxEdgeCountChange(maxEdgeCount);
110+
// Update the maxEdgeCount in the parent component based on mode
111+
const maxCountToUse = uniqueStudentMode ? maxEdgeCount : Math.max(...Object.values(totalVisits), 1);
112+
console.log("GraphvizParent: Setting maxEdgeCount to:", maxCountToUse, "for mode:", uniqueStudentMode ? "unique students" : "total visits");
113+
onMaxEdgeCountChange(maxCountToUse);
112114

113115
// Calculate and update the maximum minimum-edge count
114116
const sequenceToUse = selectedSequence || topSequences[0]?.sequence;

0 commit comments

Comments
 (0)