MEDT commited on
Commit
9440f3c
·
verified ·
1 Parent(s): 2bc2ec1

Add 2 files

Browse files
Files changed (2) hide show
  1. index.html +943 -532
  2. prompts.txt +2 -1
index.html CHANGED
@@ -3,49 +3,54 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>DataViz Dashboard</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
 
9
  <style>
10
  .dropzone {
11
  border: 2px dashed #3b82f6;
12
- border-radius: 0.5rem;
13
  transition: all 0.3s ease;
14
  }
15
  .dropzone.active {
16
  border-color: #10b981;
17
  background-color: #f0fdf4;
18
  }
19
- .chart-preview {
20
- transition: all 0.3s ease;
21
- }
22
- .chart-preview:hover {
23
- transform: scale(1.02);
24
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
25
- }
26
- .sidebar {
27
- transition: all 0.3s ease;
28
- }
29
- @media (max-width: 768px) {
30
- .sidebar {
31
- transform: translateX(-100%);
32
- position: absolute;
33
- z-index: 50;
34
- height: 100vh;
35
- }
36
- .sidebar.open {
37
- transform: translateX(0);
38
- }
39
- }
40
  .chart-container {
41
  min-height: 400px;
42
  background-color: #f8fafc;
 
 
 
 
43
  }
44
  .data-table {
45
- max-height: 300px;
46
  overflow-y: auto;
47
  }
48
- /* Custom scrollbar */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  ::-webkit-scrollbar {
50
  width: 8px;
51
  height: 8px;
@@ -60,6 +65,45 @@
60
  ::-webkit-scrollbar-thumb:hover {
61
  background: #94a3b8;
62
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  </style>
64
  </head>
65
  <body class="bg-gray-50 font-sans">
@@ -71,27 +115,41 @@
71
 
72
  <!-- Sidebar -->
73
  <div id="sidebar" class="sidebar w-64 bg-white shadow-md flex flex-col">
74
- <div class="p-4 border-b border-gray-200">
75
- <h1 class="text-2xl font-bold text-blue-600 flex items-center">
76
- <i class="fas fa-chart-line mr-2"></i> DataViz
77
- </h1>
78
- <p class="text-sm text-gray-500">Interactive Dashboard</p>
 
 
 
 
 
79
  </div>
80
 
81
- <div class="p-4 space-y-4">
82
  <div>
83
  <h3 class="font-medium text-gray-700 mb-2 flex items-center">
84
  <i class="fas fa-database mr-2 text-blue-500"></i> Data Sources
 
85
  </h3>
86
  <button id="importBtn" class="w-full bg-blue-50 hover:bg-blue-100 text-blue-600 py-2 px-4 rounded-md flex items-center justify-center">
87
  <i class="fas fa-file-import mr-2"></i> Import Data
88
  </button>
89
  <input type="file" id="fileInput" accept=".csv,.xlsx,.xls" class="hidden">
 
 
 
 
 
 
 
90
  </div>
91
 
92
  <div>
93
  <h3 class="font-medium text-gray-700 mb-2 flex items-center">
94
  <i class="fas fa-chart-pie mr-2 text-purple-500"></i> Visualization Types
 
95
  </h3>
96
  <div class="space-y-2">
97
  <button class="viz-type-btn w-full text-left py-2 px-3 rounded-md hover:bg-purple-50 flex items-center" data-type="bar">
@@ -118,6 +176,7 @@
118
  <div>
119
  <h3 class="font-medium text-gray-700 mb-2 flex items-center">
120
  <i class="fas fa-magic mr-2 text-green-500"></i> AI Suggestions
 
121
  </h3>
122
  <button id="aiSuggestBtn" class="w-full bg-green-50 hover:bg-green-100 text-green-600 py-2 px-4 rounded-md flex items-center justify-center">
123
  <i class="fas fa-robot mr-2"></i> Get AI Recommendation
@@ -127,6 +186,7 @@
127
  <div>
128
  <h3 class="font-medium text-gray-700 mb-2 flex items-center">
129
  <i class="fas fa-tasks mr-2 text-yellow-500"></i> Data Cleaning
 
130
  </h3>
131
  <div class="space-y-2">
132
  <button class="data-clean-btn w-full text-left py-2 px-3 rounded-md hover:bg-yellow-50 flex items-center" data-action="remove-duplicates">
@@ -138,19 +198,23 @@
138
  <button class="data-clean-btn w-full text-left py-2 px-3 rounded-md hover:bg-yellow-50 flex items-center" data-action="filter-data">
139
  <i class="fas fa-filter mr-2 text-yellow-500"></i> Filter Data
140
  </button>
 
 
 
141
  </div>
142
  </div>
143
 
144
- <div class="pt-4 border-t border-gray-200">
145
  <h3 class="font-medium text-gray-700 mb-2 flex items-center">
146
- <i class="fas fa-language mr-2 text-indigo-500"></i> Language
 
147
  </h3>
148
- <select class="w-full p-2 border border-gray-300 rounded-md">
149
- <option>English</option>
150
- <option>Spanish</option>
151
- <option>French</option>
152
- <option>German</option>
153
- <option>Chinese</option>
154
  </select>
155
  </div>
156
  </div>
@@ -159,6 +223,9 @@
159
  <button id="exportBtn" class="w-full bg-indigo-600 hover:bg-indigo-700 text-white py-2 px-4 rounded-md flex items-center justify-center">
160
  <i class="fas fa-file-export mr-2"></i> Export Dashboard
161
  </button>
 
 
 
162
  </div>
163
  </div>
164
 
@@ -195,10 +262,10 @@
195
  <i class="fas fa-file-import mr-2 text-blue-500"></i> Import Your Data
196
  </h3>
197
  <div class="flex space-x-2">
198
- <button class="px-3 py-1 bg-gray-100 hover:bg-gray-200 rounded-md text-sm">
199
  <i class="fas fa-question-circle mr-1"></i> Help
200
  </button>
201
- <button class="px-3 py-1 bg-gray-100 hover:bg-gray-200 rounded-md text-sm">
202
  <i class="fas fa-history mr-1"></i> Recent Files
203
  </button>
204
  </div>
@@ -230,6 +297,9 @@
230
  <button id="transformDataBtn" class="px-3 py-1 bg-purple-50 hover:bg-purple-100 text-purple-600 rounded-md text-sm flex items-center">
231
  <i class="fas fa-exchange-alt mr-1"></i> Transform
232
  </button>
 
 
 
233
  </div>
234
  </div>
235
 
@@ -247,102 +317,21 @@
247
  </div>
248
  </div>
249
 
250
- <!-- Visualization workspace -->
251
- <div id="vizWorkspace" class="bg-white rounded-lg shadow-md p-6 hidden">
252
  <div class="flex justify-between items-center mb-4">
253
  <h3 class="text-lg font-medium text-gray-800 flex items-center">
254
- <i class="fas fa-chart-area mr-2 text-purple-500"></i> Visualization Workspace
255
  </h3>
256
  <div class="flex space-x-2">
257
- <button id="saveVizBtn" class="px-3 py-1 bg-green-50 hover:bg-green-100 text-green-600 rounded-md text-sm flex items-center">
258
- <i class="fas fa-save mr-1"></i> Save
259
  </button>
260
- <button id="resetVizBtn" class="px-3 py-1 bg-red-50 hover:bg-red-100 text-red-600 rounded-md text-sm flex items-center">
261
- <i class="fas fa-redo mr-1"></i> Reset
262
  </button>
263
  </div>
264
  </div>
265
-
266
- <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
267
- <!-- Visualization controls -->
268
- <div class="lg:col-span-1 bg-gray-50 p-4 rounded-lg">
269
- <div class="space-y-4">
270
- <div>
271
- <label class="block text-sm font-medium text-gray-700 mb-1">Chart Type</label>
272
- <select id="chartTypeSelect" class="w-full p-2 border border-gray-300 rounded-md">
273
- <option value="bar">Bar Chart</option>
274
- <option value="line">Line Chart</option>
275
- <option value="pie">Pie Chart</option>
276
- <option value="scatter">Scatter Plot</option>
277
- <option value="flowchart">Flowchart</option>
278
- <option value="mindmap">Mind Map</option>
279
- </select>
280
- </div>
281
-
282
- <div>
283
- <label class="block text-sm font-medium text-gray-700 mb-1">X-Axis</label>
284
- <select id="xAxisSelect" class="w-full p-2 border border-gray-300 rounded-md">
285
- <!-- Options will be populated by JavaScript -->
286
- </select>
287
- </div>
288
-
289
- <div>
290
- <label class="block text-sm font-medium text-gray-700 mb-1">Y-Axis</label>
291
- <select id="yAxisSelect" class="w-full p-2 border border-gray-300 rounded-md">
292
- <!-- Options will be populated by JavaScript -->
293
- </select>
294
- </div>
295
-
296
- <div>
297
- <label class="block text-sm font-medium text-gray-700 mb-1">Color Scheme</label>
298
- <select id="colorSchemeSelect" class="w-full p-2 border border-gray-300 rounded-md">
299
- <option value="default">Default</option>
300
- <option value="rainbow">Rainbow</option>
301
- <option value="pastel">Pastel</option>
302
- <option value="warm">Warm</option>
303
- <option value="cool">Cool</option>
304
- <option value="monochrome">Monochrome</option>
305
- </select>
306
- </div>
307
-
308
- <div>
309
- <label class="block text-sm font-medium text-gray-700 mb-1">Chart Title</label>
310
- <input type="text" id="chartTitleInput" class="w-full p-2 border border-gray-300 rounded-md" placeholder="Enter chart title">
311
- </div>
312
-
313
- <div class="pt-2">
314
- <button id="updateChartBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-md">
315
- Update Visualization
316
- </button>
317
- </div>
318
- </div>
319
- </div>
320
-
321
- <!-- Visualization preview -->
322
- <div class="lg:col-span-2">
323
- <div class="chart-container border border-gray-200 rounded-md p-4 flex items-center justify-center">
324
- <div id="chartPlaceholder" class="text-center text-gray-500">
325
- <i class="fas fa-chart-bar text-5xl mb-3"></i>
326
- <p>Select data and chart type to generate visualization</p>
327
- </div>
328
- <canvas id="chartCanvas" class="hidden"></canvas>
329
- <div id="flowchartContainer" class="hidden w-full h-full"></div>
330
- <div id="mindmapContainer" class="hidden w-full h-full"></div>
331
- </div>
332
- </div>
333
- </div>
334
- </div>
335
-
336
- <!-- AI recommendation section -->
337
- <div id="aiRecommendationSection" class="bg-white rounded-lg shadow-md p-6 hidden">
338
- <div class="flex justify-between items-center mb-4">
339
- <h3 class="text-lg font-medium text-gray-800 flex items-center">
340
- <i class="fas fa-robot mr-2 text-green-500"></i> AI Recommendation
341
- </h3>
342
- <button id="applyAiRecommendationBtn" class="px-3 py-1 bg-green-50 hover:bg-green-100 text-green-600 rounded-md text-sm flex items-center">
343
- <i class="fas fa-check-circle mr-1"></i> Apply Recommendation
344
- </button>
345
- </div>
346
 
347
  <div class="bg-blue-50 p-4 rounded-md">
348
  <div class="flex items-start">
@@ -359,6 +348,31 @@
359
  </div>
360
  </div>
361
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  <!-- Dashboard templates -->
363
  <div id="templatesSection" class="mt-6 hidden">
364
  <div class="flex justify-between items-center mb-4">
@@ -371,7 +385,7 @@
371
  </div>
372
 
373
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
374
- <div class="template-card bg-white rounded-lg shadow-md overflow-hidden border border-gray-200 hover:border-blue-300 cursor-pointer">
375
  <div class="h-32 bg-gradient-to-r from-blue-400 to-blue-600 flex items-center justify-center">
376
  <i class="fas fa-chart-bar text-white text-4xl"></i>
377
  </div>
@@ -381,7 +395,7 @@
381
  </div>
382
  </div>
383
 
384
- <div class="template-card bg-white rounded-lg shadow-md overflow-hidden border border-gray-200 hover:border-blue-300 cursor-pointer">
385
  <div class="h-32 bg-gradient-to-r from-purple-400 to-purple-600 flex items-center justify-center">
386
  <i class="fas fa-users text-white text-4xl"></i>
387
  </div>
@@ -391,7 +405,7 @@
391
  </div>
392
  </div>
393
 
394
- <div class="template-card bg-white rounded-lg shadow-md overflow-hidden border border-gray-200 hover:border-blue-300 cursor-pointer">
395
  <div class="h-32 bg-gradient-to-r from-green-400 to-green-600 flex items-center justify-center">
396
  <i class="fas fa-project-diagram text-white text-4xl"></i>
397
  </div>
@@ -482,19 +496,19 @@
482
  </div>
483
  <div>
484
  <label class="block text-sm font-medium text-gray-700 mb-1">Quality</label>
485
- <select class="w-full p-2 border border-gray-300 rounded-md">
486
- <option>High (300dpi)</option>
487
- <option>Medium (150dpi)</option>
488
- <option>Low (72dpi)</option>
489
  </select>
490
  </div>
491
  <div>
492
  <label class="block text-sm font-medium text-gray-700 mb-1">Size</label>
493
- <select class="w-full p-2 border border-gray-300 rounded-md">
494
- <option>Original Size</option>
495
- <option>A4 (210 × 297mm)</option>
496
- <option>Letter (216 × 279mm)</option>
497
- <option>Custom...</option>
498
  </select>
499
  </div>
500
  </div>
@@ -510,22 +524,111 @@
510
  </div>
511
  </div>
512
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
513
  <script>
514
  // Sample data for demonstration
515
  let sampleData = [
516
- { id: 1, product: 'Laptop', category: 'Electronics', sales: 1200, profit: 300, region: 'North' },
517
- { id: 2, product: 'Smartphone', category: 'Electronics', sales: 800, profit: 200, region: 'South' },
518
- { id: 3, product: 'Desk', category: 'Furniture', sales: 450, profit: 150, region: 'East' },
519
- { id: 4, product: 'Chair', category: 'Furniture', sales: 250, profit: 75, region: 'West' },
520
- { id: 5, product: 'Monitor', category: 'Electronics', sales: 350, profit: 100, region: 'North' },
521
- { id: 6, product: 'Keyboard', category: 'Electronics', sales: 100, profit: 25, region: 'South' },
522
- { id: 7, product: 'Mouse', category: 'Electronics', sales: 50, profit: 15, region: 'East' },
523
- { id: 8, product: 'Table', category: 'Furniture', sales: 600, profit: 180, region: 'West' }
524
  ];
525
 
526
  // DOM elements
527
  const sidebar = document.getElementById('sidebar');
528
  const sidebarToggle = document.getElementById('sidebarToggle');
 
529
  const importBtn = document.getElementById('importBtn');
530
  const browseFilesBtn = document.getElementById('browseFilesBtn');
531
  const fileInput = document.getElementById('fileInput');
@@ -554,20 +657,47 @@
554
  const colorSchemeSelect = document.getElementById('colorSchemeSelect');
555
  const chartTitleInput = document.getElementById('chartTitleInput');
556
  const updateChartBtn = document.getElementById('updateChartBtn');
557
- const chartPlaceholder = document.getElementById('chartPlaceholder');
558
- const chartCanvas = document.getElementById('chartCanvas');
559
- const flowchartContainer = document.getElementById('flowchartContainer');
560
- const mindmapContainer = document.getElementById('mindmapContainer');
561
  const aiSuggestBtn = document.getElementById('aiSuggestBtn');
562
  const aiRecommendationText = document.getElementById('aiRecommendationText');
563
  const applyAiRecommendationBtn = document.getElementById('applyAiRecommendationBtn');
 
564
  const vizTypeBtns = document.querySelectorAll('.viz-type-btn');
565
  const dataCleanBtns = document.querySelectorAll('.data-clean-btn');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
566
 
567
  // Current state
568
  let currentData = [];
569
- let currentChart = null;
570
  let currentVizType = 'bar';
 
 
 
571
 
572
  // Initialize the app
573
  function init() {
@@ -576,6 +706,24 @@
576
  sidebar.classList.toggle('open');
577
  });
578
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
579
  // Import button click
580
  importBtn.addEventListener('click', () => {
581
  fileInput.click();
@@ -609,13 +757,7 @@
609
  btn.addEventListener('click', () => {
610
  const type = btn.dataset.type;
611
  currentVizType = type;
612
- chartTypeSelect.value = type;
613
- updateVizControls();
614
- if (currentData.length > 0) {
615
- renderVisualization();
616
- } else {
617
- alert('Please import data first');
618
- }
619
  });
620
  });
621
 
@@ -623,7 +765,13 @@
623
  dataCleanBtns.forEach(btn => {
624
  btn.addEventListener('click', () => {
625
  const action = btn.dataset.action;
626
- performDataCleaning(action);
 
 
 
 
 
 
627
  });
628
  });
629
 
@@ -638,8 +786,22 @@
638
 
639
  saveEditBtn.addEventListener('click', () => {
640
  // In a real app, we would save the edited data
 
641
  editDataModal.classList.add('hidden');
642
- alert('Data changes saved successfully!');
 
 
 
 
 
 
 
 
 
 
 
 
 
643
  });
644
 
645
  // Export modal
@@ -656,30 +818,92 @@
656
  });
657
 
658
  confirmExportBtn.addEventListener('click', () => {
 
 
 
 
 
659
  exportModal.classList.add('hidden');
660
- alert('Export initiated! In a real app, this would download the file.');
661
  });
662
 
663
  exportFormatBtns.forEach(btn => {
664
  btn.addEventListener('click', () => {
665
- const format = btn.dataset.format;
666
- alert(`Preparing to export as ${format.toUpperCase()}`);
667
  });
668
  });
669
 
670
- // Chart controls
671
- chartTypeSelect.addEventListener('change', () => {
672
- currentVizType = chartTypeSelect.value;
673
- updateVizControls();
674
- });
675
-
676
- updateChartBtn.addEventListener('click', renderVisualization);
677
-
678
  // AI suggestion
679
  aiSuggestBtn.addEventListener('click', generateAiRecommendation);
680
 
681
  applyAiRecommendationBtn.addEventListener('click', applyAiRecommendation);
682
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
683
  // Load sample data for demo
684
  loadSampleData();
685
  }
@@ -687,11 +911,12 @@
687
  // Load sample data for demonstration
688
  function loadSampleData() {
689
  currentData = [...sampleData];
 
690
  renderDataPreview();
691
  dataPreviewSection.classList.remove('hidden');
692
  vizWorkspace.classList.remove('hidden');
693
  templatesSection.classList.remove('hidden');
694
- updateVizControls();
695
  }
696
 
697
  // Handle file selection
@@ -717,17 +942,86 @@
717
 
718
  if (fileType === 'csv') {
719
  // In a real app, we would parse the CSV file
720
- alert(`CSV file "${file.name}" selected. In a real app, we would parse this file.`);
721
- loadSampleData(); // For demo purposes
 
722
  } else if (fileType === 'xlsx' || fileType === 'xls') {
723
  // In a real app, we would parse the Excel file
724
- alert(`Excel file "${file.name}" selected. In a real app, we would parse this file.`);
725
- loadSampleData(); // For demo purposes
 
726
  } else {
727
  alert('Unsupported file type. Please upload a CSV or Excel file.');
728
  }
729
  }
730
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
731
  // Prevent default drag and drop behaviors
732
  function preventDefaults(e) {
733
  e.preventDefault();
@@ -777,355 +1071,342 @@
777
  });
778
  }
779
 
780
- // Update visualization controls based on current data and type
781
- function updateVizControls() {
782
- if (currentData.length === 0) return;
 
 
 
 
 
 
 
783
 
784
  // Get headers from first object
785
  const headers = Object.keys(currentData[0]);
786
 
787
- // Clear existing options
788
- xAxisSelect.innerHTML = '';
789
- yAxisSelect.innerHTML = '';
 
 
 
 
790
 
791
- // Add options based on chart type
792
- if (currentVizType === 'bar' || currentVizType === 'line') {
793
- headers.forEach(header => {
794
- xAxisSelect.add(new Option(header, header));
795
- });
796
-
797
- // For Y-axis, only include numeric columns
798
- headers.forEach(header => {
799
- if (typeof currentData[0][header] === 'number') {
800
- yAxisSelect.add(new Option(header, header));
801
- }
802
- });
803
-
804
- // Set default selections
805
- xAxisSelect.value = headers[0];
806
- yAxisSelect.value = headers.find(h => typeof currentData[0][h] === 'number');
807
- }
808
- else if (currentVizType === 'pie') {
809
- headers.forEach(header => {
810
- xAxisSelect.add(new Option(header, header));
811
- });
812
-
813
- // For pie charts, only include numeric columns for values
814
  headers.forEach(header => {
815
- if (typeof currentData[0][header] === 'number') {
816
- yAxisSelect.add(new Option(header, header));
817
- }
 
 
 
 
 
 
 
 
 
818
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
819
 
820
- // Set default selections
821
- xAxisSelect.value = headers[0];
822
- yAxisSelect.value = headers.find(h => typeof currentData[0][h] === 'number');
823
- }
824
- else if (currentVizType === 'scatter') {
825
- // For scatter plots, both axes should be numeric
826
- headers.forEach(header => {
827
- if (typeof currentData[0][header] === 'number') {
828
- xAxisSelect.add(new Option(header, header));
829
- yAxisSelect.add(new Option(header, header));
830
- }
831
- });
832
 
833
- // Set default selections
834
- const numericHeaders = headers.filter(h => typeof currentData[0][h] === 'number');
835
- xAxisSelect.value = numericHeaders[0];
836
- yAxisSelect.value = numericHeaders[1] || numericHeaders[0];
837
- }
838
- else if (currentVizType === 'flowchart' || currentVizType === 'mindmap') {
839
- // For diagrams, we need different controls
840
- // Simplified for this demo
841
- xAxisSelect.innerHTML = '<option value="nodes">Nodes</option>';
842
- yAxisSelect.innerHTML = '<option value="connections">Connections</option>';
843
- }
844
  }
845
 
846
- // Render visualization based on current selections
847
- function renderVisualization() {
848
- if (currentData.length === 0) return;
 
 
 
849
 
850
- // Hide all containers first
851
- chartPlaceholder.classList.add('hidden');
852
- chartCanvas.classList.add('hidden');
853
- flowchartContainer.classList.add('hidden');
854
- mindmapContainer.classList.add('hidden');
855
-
856
- // Get selected values
857
- const xAxis = xAxisSelect.value;
858
- const yAxis = yAxisSelect.value;
859
- const colorScheme = colorSchemeSelect.value;
860
- const title = chartTitleInput.value || `${currentVizType} Chart`;
861
-
862
- if (currentVizType === 'bar' || currentVizType === 'line' || currentVizType === 'pie' || currentVizType === 'scatter') {
863
- // Show chart canvas
864
- chartCanvas.classList.remove('hidden');
865
-
866
- // In a real app, we would use a charting library like Chart.js
867
- // For this demo, we'll just show a placeholder
868
- const ctx = chartCanvas.getContext('2d');
869
-
870
- // Clear previous chart if exists
871
- if (currentChart) {
872
- currentChart.destroy();
 
 
 
 
 
 
 
 
 
873
  }
874
 
875
- // Create mock chart data based on selections
876
- const labels = currentData.map(item => item[xAxis]);
877
- const data = currentData.map(item => item[yAxis]);
878
-
879
- // Generate mock chart
880
- currentChart = {
881
- destroy: function() {
882
- // Mock destroy function
883
- }
884
- };
885
-
886
- // Show a message about what would be rendered
887
- chartCanvas.width = chartCanvas.offsetWidth;
888
- chartCanvas.height = chartCanvas.offsetHeight;
889
-
890
- ctx.fillStyle = '#f8fafc';
891
- ctx.fillRect(0, 0, chartCanvas.width, chartCanvas.height);
892
-
893
- ctx.font = '16px Arial';
894
- ctx.fillStyle = '#334155';
895
- ctx.textAlign = 'center';
896
- ctx.fillText(`${title} (${currentVizType})`, chartCanvas.width / 2, 30);
897
-
898
- ctx.font = '14px Arial';
899
- ctx.fillText(`X-Axis: ${xAxis}`, chartCanvas.width / 2, 60);
900
- ctx.fillText(`Y-Axis: ${yAxis}`, chartCanvas.width / 2, 80);
901
-
902
- // Draw a simple representation of the chart
903
- if (currentVizType === 'bar') {
904
- drawMockBarChart(ctx, labels, data);
905
- } else if (currentVizType === 'line') {
906
- drawMockLineChart(ctx, labels, data);
907
- } else if (currentVizType === 'pie') {
908
- drawMockPieChart(ctx, labels, data);
909
- } else if (currentVizType === 'scatter') {
910
- drawMockScatterPlot(ctx, labels, data);
911
  }
912
- }
913
- else if (currentVizType === 'flowchart') {
914
- // Show flowchart container
915
- flowchartContainer.classList.remove('hidden');
916
-
917
- // In a real app, we would use a diagramming library
918
- flowchartContainer.innerHTML = `
919
- <div class="text-center p-4">
920
- <i class="fas fa-project-diagram text-4xl text-blue-500 mb-2"></i>
921
- <h4 class="text-lg font-medium">Flowchart</h4>
922
- <p class="text-sm text-gray-600">This would display a flowchart based on your data</p>
923
- </div>
924
- `;
925
- }
926
- else if (currentVizType === 'mindmap') {
927
- // Show mindmap container
928
- mindmapContainer.classList.remove('hidden');
929
-
930
- // In a real app, we would use a diagramming library
931
- mindmapContainer.innerHTML = `
932
- <div class="text-center p-4">
933
- <i class="fas fa-sitemap text-4xl text-green-500 mb-2"></i>
934
- <h4 class="text-lg font-medium">Mind Map</h4>
935
- <p class="text-sm text-gray-600">This would display a mind map based on your data</p>
936
- </div>
937
- `;
938
  }
 
 
 
 
 
 
 
 
 
 
 
 
939
  }
940
 
941
- // Draw a mock bar chart (for demo purposes)
942
- function drawMockBarChart(ctx, labels, data) {
943
- const maxValue = Math.max(...data);
944
- const barWidth = 40;
945
- const startX = 60;
946
- const startY = 100;
947
- const chartHeight = ctx.canvas.height - 150;
948
- const chartWidth = ctx.canvas.width - 120;
949
-
950
- // Draw axes
951
- ctx.strokeStyle = '#94a3b8';
952
- ctx.lineWidth = 1;
953
 
954
- // Y-axis
955
- ctx.beginPath();
956
- ctx.moveTo(startX, startY);
957
- ctx.lineTo(startX, startY + chartHeight);
958
- ctx.stroke();
959
 
960
- // X-axis
961
- ctx.beginPath();
962
- ctx.moveTo(startX, startY + chartHeight);
963
- ctx.lineTo(startX + chartWidth, startY + chartHeight);
964
- ctx.stroke();
965
 
966
- // Draw bars
967
- const spacing = (chartWidth - (labels.length * barWidth)) / (labels.length + 1);
968
- let x = startX + spacing;
 
 
 
 
 
 
969
 
970
- for (let i = 0; i < labels.length; i++) {
971
- const barHeight = (data[i] / maxValue) * chartHeight;
972
- const y = startY + chartHeight - barHeight;
973
-
974
- ctx.fillStyle = getBarColor(i);
975
- ctx.fillRect(x, y, barWidth, barHeight);
976
-
977
- // Draw label
978
- ctx.font = '12px Arial';
979
- ctx.fillStyle = '#334155';
980
- ctx.textAlign = 'center';
981
- ctx.fillText(labels[i], x + barWidth/2, startY + chartHeight + 20);
982
-
983
- x += barWidth + spacing;
 
 
 
 
 
 
 
 
 
984
  }
 
 
 
 
985
  }
986
 
987
- // Draw a mock line chart (for demo purposes)
988
- function drawMockLineChart(ctx, labels, data) {
989
- const maxValue = Math.max(...data);
990
- const startX = 60;
991
- const startY = 100;
992
- const chartHeight = ctx.canvas.height - 150;
993
- const chartWidth = ctx.canvas.width - 120;
994
 
995
- // Draw axes
996
- ctx.strokeStyle = '#94a3b8';
997
- ctx.lineWidth = 1;
 
 
 
 
 
998
 
999
- // Y-axis
1000
- ctx.beginPath();
1001
- ctx.moveTo(startX, startY);
1002
- ctx.lineTo(startX, startY + chartHeight);
1003
- ctx.stroke();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1004
 
1005
- // X-axis
1006
- ctx.beginPath();
1007
- ctx.moveTo(startX, startY + chartHeight);
1008
- ctx.lineTo(startX + chartWidth, startY + chartHeight);
1009
- ctx.stroke();
1010
 
1011
- // Draw line
1012
- ctx.beginPath();
1013
- ctx.strokeStyle = '#3b82f6';
1014
- ctx.lineWidth = 2;
1015
 
1016
- const pointSpacing = chartWidth / (labels.length - 1);
1017
- let x = startX;
 
 
 
 
1018
 
1019
- for (let i = 0; i < labels.length; i++) {
1020
- const y = startY + chartHeight - (data[i] / maxValue) * chartHeight;
1021
-
1022
- if (i === 0) {
1023
- ctx.moveTo(x, y);
1024
- } else {
1025
- ctx.lineTo(x, y);
1026
  }
1027
-
1028
- // Draw point
1029
- ctx.fillStyle = '#3b82f6';
1030
- ctx.beginPath();
1031
- ctx.arc(x, y, 4, 0, Math.PI * 2);
1032
- ctx.fill();
1033
-
1034
- // Draw label
1035
- ctx.font = '12px Arial';
1036
- ctx.fillStyle = '#334155';
1037
- ctx.textAlign = 'center';
1038
- ctx.fillText(labels[i], x, startY + chartHeight + 20);
1039
-
1040
- x += pointSpacing;
1041
- }
1042
 
1043
- ctx.stroke();
 
1044
  }
1045
 
1046
- // Draw a mock pie chart (for demo purposes)
1047
- function drawMockPieChart(ctx, labels, data) {
1048
- const centerX = ctx.canvas.width / 2;
1049
- const centerY = ctx.canvas.height / 2;
1050
- const radius = Math.min(ctx.canvas.width, ctx.canvas.height) / 3;
1051
- const total = data.reduce((sum, value) => sum + value, 0);
1052
 
1053
- let startAngle = 0;
 
1054
 
1055
- for (let i = 0; i < data.length; i++) {
1056
- const sliceAngle = (data[i] / total) * 2 * Math.PI;
1057
-
1058
- ctx.fillStyle = getBarColor(i);
1059
- ctx.beginPath();
1060
- ctx.moveTo(centerX, centerY);
1061
- ctx.arc(centerX, centerY, radius, startAngle, startAngle + sliceAngle);
1062
- ctx.closePath();
1063
- ctx.fill();
1064
-
1065
- // Draw label outside
1066
- const midAngle = startAngle + sliceAngle / 2;
1067
- const labelX = centerX + (radius + 20) * Math.cos(midAngle);
1068
- const labelY = centerY + (radius + 20) * Math.sin(midAngle);
1069
-
1070
- ctx.font = '12px Arial';
1071
- ctx.fillStyle = '#334155';
1072
- ctx.textAlign = 'center';
1073
- ctx.fillText(labels[i], labelX, labelY);
1074
-
1075
- startAngle += sliceAngle;
1076
  }
1077
- }
1078
-
1079
- // Draw a mock scatter plot (for demo purposes)
1080
- function drawMockScatterPlot(ctx, labels, data) {
1081
- const maxValue = Math.max(...data);
1082
- const startX = 60;
1083
- const startY = 100;
1084
- const chartHeight = ctx.canvas.height - 150;
1085
- const chartWidth = ctx.canvas.width - 120;
1086
-
1087
- // Draw axes
1088
- ctx.strokeStyle = '#94a3b8';
1089
- ctx.lineWidth = 1;
1090
 
1091
- // Y-axis
1092
- ctx.beginPath();
1093
- ctx.moveTo(startX, startY);
1094
- ctx.lineTo(startX, startY + chartHeight);
1095
- ctx.stroke();
1096
 
1097
- // X-axis
1098
- ctx.beginPath();
1099
- ctx.moveTo(startX, startY + chartHeight);
1100
- ctx.lineTo(startX + chartWidth, startY + chartHeight);
1101
- ctx.stroke();
1102
 
1103
- // Draw points
1104
- const pointSpacing = chartWidth / (labels.length - 1);
1105
- let x = startX;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1106
 
1107
- for (let i = 0; i < labels.length; i++) {
1108
- const y = startY + chartHeight - (data[i] / maxValue) * chartHeight;
1109
-
1110
- ctx.fillStyle = getBarColor(i);
1111
- ctx.beginPath();
1112
- ctx.arc(x, y, 6, 0, Math.PI * 2);
1113
- ctx.fill();
1114
-
1115
- // Draw label
1116
- ctx.font = '12px Arial';
1117
- ctx.fillStyle = '#334155';
1118
- ctx.textAlign = 'center';
1119
- ctx.fillText(labels[i], x, startY + chartHeight + 20);
1120
-
1121
- x += pointSpacing;
1122
- }
1123
  }
1124
 
1125
- // Get color for bars/points based on index and selected color scheme
1126
- function getBarColor(index) {
1127
- const scheme = colorSchemeSelect.value;
1128
-
1129
  const schemes = {
1130
  default: ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6'],
1131
  rainbow: ['#ff0000', '#ff7f00', '#ffff00', '#00ff00', '#0000ff', '#4b0082', '#9400d3'],
@@ -1136,7 +1417,131 @@
1136
  };
1137
 
1138
  const colors = schemes[scheme] || schemes.default;
1139
- return colors[index % colors.length];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1140
  }
1141
 
1142
  // Generate AI recommendation
@@ -1151,16 +1556,27 @@
1151
  const headers = Object.keys(currentData[0]);
1152
  const numericHeaders = headers.filter(h => typeof currentData[0][h] === 'number');
1153
  const categoricalHeaders = headers.filter(h => typeof currentData[0][h] === 'string');
 
1154
 
1155
  let recommendation = '';
1156
 
1157
- if (numericHeaders.length >= 2 && categoricalHeaders.length >= 1) {
 
 
 
 
1158
  recommendation = `A grouped bar chart comparing ${numericHeaders[0]} and ${numericHeaders[1]} across different ${categoricalHeaders[0]} categories would effectively show the relationships in your data.`;
1159
- } else if (numericHeaders.length >= 1 && categoricalHeaders.length >= 1) {
 
 
1160
  recommendation = `A pie chart showing the distribution of ${numericHeaders[0]} by ${categoricalHeaders[0]} would be a good choice to visualize proportions.`;
1161
- } else if (numericHeaders.length >= 2) {
 
 
1162
  recommendation = `A scatter plot with ${numericHeaders[0]} on the X-axis and ${numericHeaders[1]} on the Y-axis would help identify correlations.`;
1163
- } else {
 
 
1164
  recommendation = `Based on your data structure, a simple table might be the most effective way to present this information.`;
1165
  }
1166
 
@@ -1170,52 +1586,47 @@
1170
 
1171
  // Apply AI recommendation
1172
  function applyAiRecommendation() {
1173
- // In a real app, this would set the appropriate chart type and axes
1174
- // For demo, we'll just set to bar chart
1175
- currentVizType = 'bar';
1176
- chartTypeSelect.value = 'bar';
1177
- updateVizControls();
1178
- renderVisualization();
1179
-
1180
- // Scroll to visualization
1181
- vizWorkspace.scrollIntoView({ behavior: 'smooth' });
1182
  }
1183
 
1184
- // Perform data cleaning action
1185
- function performDataCleaning(action) {
1186
- if (currentData.length === 0) {
1187
- alert('Please import data first');
1188
- return;
1189
- }
1190
-
1191
- switch(action) {
1192
- case 'remove-duplicates':
1193
- // In a real app, we would actually remove duplicates
1194
- alert('Duplicate rows removed successfully!');
1195
- break;
1196
- case 'fill-missing':
1197
- // In a real app, we would fill missing values
1198
- alert('Missing values filled successfully!');
1199
- break;
1200
- case 'filter-data':
1201
- // In a real app, we would show filtering UI
1202
- alert('Filter applied successfully!');
1203
- break;
1204
- default:
1205
- alert('Unknown action');
1206
- }
1207
-
1208
- // Refresh the preview
1209
- renderDataPreview();
1210
-
1211
- // If visualization exists, update it
1212
- if (!chartPlaceholder.classList.contains('hidden')) {
1213
- renderVisualization();
1214
  }
1215
  }
1216
 
1217
- // Initialize the app
1218
- init();
1219
- </script>
1220
- <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=MEDT/dataviz-v2" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
 
 
 
 
 
 
 
 
1221
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>DataViz Pro | Interactive Dashboard</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10
  <style>
11
  .dropzone {
12
  border: 2px dashed #3b82f6;
13
+ border-radius: 0.75rem;
14
  transition: all 0.3s ease;
15
  }
16
  .dropzone.active {
17
  border-color: #10b981;
18
  background-color: #f0fdf4;
19
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  .chart-container {
21
  min-height: 400px;
22
  background-color: #f8fafc;
23
+ transition: all 0.3s ease;
24
+ }
25
+ .chart-container:hover {
26
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
27
  }
28
  .data-table {
29
+ max-height: 400px;
30
  overflow-y: auto;
31
  }
32
+ .grid-stack-item {
33
+ background: white;
34
+ border-radius: 0.5rem;
35
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
36
+ }
37
+ .grid-stack-item-content {
38
+ padding: 1rem;
39
+ overflow: hidden;
40
+ }
41
+ .tooltip-icon {
42
+ display: inline-flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ width: 18px;
46
+ height: 18px;
47
+ border-radius: 50%;
48
+ background-color: #e2e8f0;
49
+ color: #64748b;
50
+ font-size: 12px;
51
+ cursor: help;
52
+ margin-left: 4px;
53
+ }
54
  ::-webkit-scrollbar {
55
  width: 8px;
56
  height: 8px;
 
65
  ::-webkit-scrollbar-thumb:hover {
66
  background: #94a3b8;
67
  }
68
+ [data-tooltip] {
69
+ position: relative;
70
+ }
71
+ [data-tooltip]:hover::after {
72
+ content: attr(data-tooltip);
73
+ position: absolute;
74
+ bottom: 100%;
75
+ left: 50%;
76
+ transform: translateX(-50%);
77
+ background: #334155;
78
+ color: white;
79
+ padding: 4px 8px;
80
+ border-radius: 4px;
81
+ font-size: 12px;
82
+ white-space: nowrap;
83
+ z-index: 100;
84
+ }
85
+ .dark-mode {
86
+ background-color: #1e293b;
87
+ color: #f8fafc;
88
+ }
89
+ .dark-mode .bg-white {
90
+ background-color: #334155 !important;
91
+ }
92
+ .dark-mode .text-gray-800 {
93
+ color: #f8fafc !important;
94
+ }
95
+ .dark-mode .text-gray-500 {
96
+ color: #94a3b8 !important;
97
+ }
98
+ .dark-mode .border-gray-200 {
99
+ border-color: #475569 !important;
100
+ }
101
+ .dark-mode .bg-gray-50 {
102
+ background-color: #1e293b !important;
103
+ }
104
+ .dark-mode .chart-container {
105
+ background-color: #1e293b !important;
106
+ }
107
  </style>
108
  </head>
109
  <body class="bg-gray-50 font-sans">
 
115
 
116
  <!-- Sidebar -->
117
  <div id="sidebar" class="sidebar w-64 bg-white shadow-md flex flex-col">
118
+ <div class="p-4 border-b border-gray-200 flex justify-between items-center">
119
+ <div>
120
+ <h1 class="text-2xl font-bold text-blue-600 flex items-center">
121
+ <i class="fas fa-chart-line mr-2"></i> DataViz Pro
122
+ </h1>
123
+ <p class="text-sm text-gray-500">Interactive Dashboard</p>
124
+ </div>
125
+ <button id="themeToggle" class="p-2 rounded-full hover:bg-gray-100">
126
+ <i class="fas fa-moon text-gray-600"></i>
127
+ </button>
128
  </div>
129
 
130
+ <div class="p-4 space-y-4 overflow-y-auto flex-1">
131
  <div>
132
  <h3 class="font-medium text-gray-700 mb-2 flex items-center">
133
  <i class="fas fa-database mr-2 text-blue-500"></i> Data Sources
134
+ <span class="tooltip-icon" data-tooltip="Import your data files">?</span>
135
  </h3>
136
  <button id="importBtn" class="w-full bg-blue-50 hover:bg-blue-100 text-blue-600 py-2 px-4 rounded-md flex items-center justify-center">
137
  <i class="fas fa-file-import mr-2"></i> Import Data
138
  </button>
139
  <input type="file" id="fileInput" accept=".csv,.xlsx,.xls" class="hidden">
140
+
141
+ <div id="recentFiles" class="mt-3 hidden">
142
+ <h4 class="text-sm font-medium text-gray-700 mb-1">Recent Files</h4>
143
+ <ul class="space-y-1 text-sm text-gray-600">
144
+ <!-- Recent files will be populated here -->
145
+ </ul>
146
+ </div>
147
  </div>
148
 
149
  <div>
150
  <h3 class="font-medium text-gray-700 mb-2 flex items-center">
151
  <i class="fas fa-chart-pie mr-2 text-purple-500"></i> Visualization Types
152
+ <span class="tooltip-icon" data-tooltip="Select chart type">?</span>
153
  </h3>
154
  <div class="space-y-2">
155
  <button class="viz-type-btn w-full text-left py-2 px-3 rounded-md hover:bg-purple-50 flex items-center" data-type="bar">
 
176
  <div>
177
  <h3 class="font-medium text-gray-700 mb-2 flex items-center">
178
  <i class="fas fa-magic mr-2 text-green-500"></i> AI Suggestions
179
+ <span class="tooltip-icon" data-tooltip="Get AI-powered recommendations">?</span>
180
  </h3>
181
  <button id="aiSuggestBtn" class="w-full bg-green-50 hover:bg-green-100 text-green-600 py-2 px-4 rounded-md flex items-center justify-center">
182
  <i class="fas fa-robot mr-2"></i> Get AI Recommendation
 
186
  <div>
187
  <h3 class="font-medium text-gray-700 mb-2 flex items-center">
188
  <i class="fas fa-tasks mr-2 text-yellow-500"></i> Data Cleaning
189
+ <span class="tooltip-icon" data-tooltip="Clean and transform your data">?</span>
190
  </h3>
191
  <div class="space-y-2">
192
  <button class="data-clean-btn w-full text-left py-2 px-3 rounded-md hover:bg-yellow-50 flex items-center" data-action="remove-duplicates">
 
198
  <button class="data-clean-btn w-full text-left py-2 px-3 rounded-md hover:bg-yellow-50 flex items-center" data-action="filter-data">
199
  <i class="fas fa-filter mr-2 text-yellow-500"></i> Filter Data
200
  </button>
201
+ <button class="data-clean-btn w-full text-left py-2 px-3 rounded-md hover:bg-yellow-50 flex items-center" data-action="add-column">
202
+ <i class="fas fa-plus-circle mr-2 text-yellow-500"></i> Add Calculated Column
203
+ </button>
204
  </div>
205
  </div>
206
 
207
+ <div>
208
  <h3 class="font-medium text-gray-700 mb-2 flex items-center">
209
+ <i class="fas fa-clone mr-2 text-indigo-500"></i> Dashboard Templates
210
+ <span class="tooltip-icon" data-tooltip="Start with a pre-built template">?</span>
211
  </h3>
212
+ <select id="templateSelect" class="w-full p-2 border border-gray-300 rounded-md">
213
+ <option value="">Select a template...</option>
214
+ <option value="sales">Sales Dashboard</option>
215
+ <option value="customer">Customer Analytics</option>
216
+ <option value="process">Process Flow</option>
217
+ <option value="financial">Financial Report</option>
218
  </select>
219
  </div>
220
  </div>
 
223
  <button id="exportBtn" class="w-full bg-indigo-600 hover:bg-indigo-700 text-white py-2 px-4 rounded-md flex items-center justify-center">
224
  <i class="fas fa-file-export mr-2"></i> Export Dashboard
225
  </button>
226
+ <button id="saveBtn" class="w-full mt-2 bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-md flex items-center justify-center">
227
+ <i class="fas fa-save mr-2"></i> Save Dashboard
228
+ </button>
229
  </div>
230
  </div>
231
 
 
262
  <i class="fas fa-file-import mr-2 text-blue-500"></i> Import Your Data
263
  </h3>
264
  <div class="flex space-x-2">
265
+ <button class="px-3 py-1 bg-gray-100 hover:bg-gray-200 rounded-md text-sm flex items-center">
266
  <i class="fas fa-question-circle mr-1"></i> Help
267
  </button>
268
+ <button id="recentFilesBtn" class="px-3 py-1 bg-gray-100 hover:bg-gray-200 rounded-md text-sm flex items-center">
269
  <i class="fas fa-history mr-1"></i> Recent Files
270
  </button>
271
  </div>
 
297
  <button id="transformDataBtn" class="px-3 py-1 bg-purple-50 hover:bg-purple-100 text-purple-600 rounded-md text-sm flex items-center">
298
  <i class="fas fa-exchange-alt mr-1"></i> Transform
299
  </button>
300
+ <button id="hideDataBtn" class="px-3 py-1 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-md text-sm flex items-center">
301
+ <i class="fas fa-eye-slash mr-1"></i> Hide
302
+ </button>
303
  </div>
304
  </div>
305
 
 
317
  </div>
318
  </div>
319
 
320
+ <!-- AI recommendation section -->
321
+ <div id="aiRecommendationSection" class="mb-6 bg-white rounded-lg shadow-md p-6 hidden">
322
  <div class="flex justify-between items-center mb-4">
323
  <h3 class="text-lg font-medium text-gray-800 flex items-center">
324
+ <i class="fas fa-robot mr-2 text-green-500"></i> AI Recommendation
325
  </h3>
326
  <div class="flex space-x-2">
327
+ <button id="applyAiRecommendationBtn" class="px-3 py-1 bg-green-50 hover:bg-green-100 text-green-600 rounded-md text-sm flex items-center">
328
+ <i class="fas fa-check-circle mr-1"></i> Apply
329
  </button>
330
+ <button id="dismissAiRecommendationBtn" class="px-3 py-1 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-md text-sm flex items-center">
331
+ <i class="fas fa-times mr-1"></i> Dismiss
332
  </button>
333
  </div>
334
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
 
336
  <div class="bg-blue-50 p-4 rounded-md">
337
  <div class="flex items-start">
 
348
  </div>
349
  </div>
350
 
351
+ <!-- Visualization workspace -->
352
+ <div id="vizWorkspace" class="bg-white rounded-lg shadow-md p-6 hidden">
353
+ <div class="flex justify-between items-center mb-4">
354
+ <h3 class="text-lg font-medium text-gray-800 flex items-center">
355
+ <i class="fas fa-chart-area mr-2 text-purple-500"></i> Visualization Workspace
356
+ </h3>
357
+ <div class="flex space-x-2">
358
+ <button id="addNewVizBtn" class="px-3 py-1 bg-blue-50 hover:bg-blue-100 text-blue-600 rounded-md text-sm flex items-center">
359
+ <i class="fas fa-plus mr-1"></i> Add Visualization
360
+ </button>
361
+ <button id="saveVizBtn" class="px-3 py-1 bg-green-50 hover:bg-green-100 text-green-600 rounded-md text-sm flex items-center">
362
+ <i class="fas fa-save mr-1"></i> Save
363
+ </button>
364
+ <button id="resetVizBtn" class="px-3 py-1 bg-red-50 hover:bg-red-100 text-red-600 rounded-md text-sm flex items-center">
365
+ <i class="fas fa-redo mr-1"></i> Reset
366
+ </button>
367
+ </div>
368
+ </div>
369
+
370
+ <!-- Grid container for multiple visualizations -->
371
+ <div id="gridContainer" class="grid-stack">
372
+ <!-- Visualizations will be added here -->
373
+ </div>
374
+ </div>
375
+
376
  <!-- Dashboard templates -->
377
  <div id="templatesSection" class="mt-6 hidden">
378
  <div class="flex justify-between items-center mb-4">
 
385
  </div>
386
 
387
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
388
+ <div class="template-card bg-white rounded-lg shadow-md overflow-hidden border border-gray-200 hover:border-blue-300 cursor-pointer" data-template="sales">
389
  <div class="h-32 bg-gradient-to-r from-blue-400 to-blue-600 flex items-center justify-center">
390
  <i class="fas fa-chart-bar text-white text-4xl"></i>
391
  </div>
 
395
  </div>
396
  </div>
397
 
398
+ <div class="template-card bg-white rounded-lg shadow-md overflow-hidden border border-gray-200 hover:border-blue-300 cursor-pointer" data-template="customer">
399
  <div class="h-32 bg-gradient-to-r from-purple-400 to-purple-600 flex items-center justify-center">
400
  <i class="fas fa-users text-white text-4xl"></i>
401
  </div>
 
405
  </div>
406
  </div>
407
 
408
+ <div class="template-card bg-white rounded-lg shadow-md overflow-hidden border border-gray-200 hover:border-blue-300 cursor-pointer" data-template="process">
409
  <div class="h-32 bg-gradient-to-r from-green-400 to-green-600 flex items-center justify-center">
410
  <i class="fas fa-project-diagram text-white text-4xl"></i>
411
  </div>
 
496
  </div>
497
  <div>
498
  <label class="block text-sm font-medium text-gray-700 mb-1">Quality</label>
499
+ <select id="exportQuality" class="w-full p-2 border border-gray-300 rounded-md">
500
+ <option value="high">High (300dpi)</option>
501
+ <option value="medium">Medium (150dpi)</option>
502
+ <option value="low">Low (72dpi)</option>
503
  </select>
504
  </div>
505
  <div>
506
  <label class="block text-sm font-medium text-gray-700 mb-1">Size</label>
507
+ <select id="exportSize" class="w-full p-2 border border-gray-300 rounded-md">
508
+ <option value="original">Original Size</option>
509
+ <option value="a4">A4 (210 × 297mm)</option>
510
+ <option value="letter">Letter (216 × 279mm)</option>
511
+ <option value="custom">Custom...</option>
512
  </select>
513
  </div>
514
  </div>
 
524
  </div>
525
  </div>
526
 
527
+ <!-- Modal for adding calculated column -->
528
+ <div id="addColumnModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
529
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-md">
530
+ <div class="p-4 border-b border-gray-200 flex justify-between items-center">
531
+ <h3 class="text-lg font-medium text-gray-800">Add Calculated Column</h3>
532
+ <button id="closeAddColumnModalBtn" class="text-gray-500 hover:text-gray-700">
533
+ <i class="fas fa-times"></i>
534
+ </button>
535
+ </div>
536
+ <div class="p-4">
537
+ <div class="space-y-4">
538
+ <div>
539
+ <label class="block text-sm font-medium text-gray-700 mb-1">Column Name</label>
540
+ <input type="text" id="newColumnName" class="w-full p-2 border border-gray-300 rounded-md" placeholder="Enter column name">
541
+ </div>
542
+ <div>
543
+ <label class="block text-sm font-medium text-gray-700 mb-1">Formula</label>
544
+ <input type="text" id="columnFormula" class="w-full p-2 border border-gray-300 rounded-md" placeholder="e.g., Sales * 0.2">
545
+ <p class="text-xs text-gray-500 mt-1">Use existing column names in your formula</p>
546
+ </div>
547
+ <div>
548
+ <label class="block text-sm font-medium text-gray-700 mb-1">Data Type</label>
549
+ <select id="columnType" class="w-full p-2 border border-gray-300 rounded-md">
550
+ <option value="number">Number</option>
551
+ <option value="text">Text</option>
552
+ <option value="date">Date</option>
553
+ <option value="boolean">True/False</option>
554
+ </select>
555
+ </div>
556
+ </div>
557
+ </div>
558
+ <div class="p-4 border-t border-gray-200 flex justify-end space-x-3">
559
+ <button id="cancelAddColumnBtn" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50">
560
+ Cancel
561
+ </button>
562
+ <button id="confirmAddColumnBtn" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">
563
+ Add Column
564
+ </button>
565
+ </div>
566
+ </div>
567
+ </div>
568
+
569
+ <!-- Modal for filter options -->
570
+ <div id="filterModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
571
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-md">
572
+ <div class="p-4 border-b border-gray-200 flex justify-between items-center">
573
+ <h3 class="text-lg font-medium text-gray-800">Filter Data</h3>
574
+ <button id="closeFilterModalBtn" class="text-gray-500 hover:text-gray-700">
575
+ <i class="fas fa-times"></i>
576
+ </button>
577
+ </div>
578
+ <div class="p-4">
579
+ <div class="space-y-4">
580
+ <div>
581
+ <label class="block text-sm font-medium text-gray-700 mb-1">Column to Filter</label>
582
+ <select id="filterColumn" class="w-full p-2 border border-gray-300 rounded-md">
583
+ <!-- Options will be populated by JavaScript -->
584
+ </select>
585
+ </div>
586
+ <div>
587
+ <label class="block text-sm font-medium text-gray-700 mb-1">Filter Condition</label>
588
+ <select id="filterCondition" class="w-full p-2 border border-gray-300 rounded-md">
589
+ <option value="equals">Equals</option>
590
+ <option value="not-equals">Does not equal</option>
591
+ <option value="contains">Contains</option>
592
+ <option value="greater">Greater than</option>
593
+ <option value="less">Less than</option>
594
+ <option value="empty">Is empty</option>
595
+ <option value="not-empty">Is not empty</option>
596
+ </select>
597
+ </div>
598
+ <div id="filterValueContainer">
599
+ <label class="block text-sm font-medium text-gray-700 mb-1">Value</label>
600
+ <input type="text" id="filterValue" class="w-full p-2 border border-gray-300 rounded-md" placeholder="Enter value">
601
+ </div>
602
+ </div>
603
+ </div>
604
+ <div class="p-4 border-t border-gray-200 flex justify-end space-x-3">
605
+ <button id="cancelFilterBtn" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50">
606
+ Cancel
607
+ </button>
608
+ <button id="applyFilterBtn" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">
609
+ Apply Filter
610
+ </button>
611
+ </div>
612
+ </div>
613
+ </div>
614
+
615
  <script>
616
  // Sample data for demonstration
617
  let sampleData = [
618
+ { id: 1, product: 'Laptop', category: 'Electronics', sales: 1200, profit: 300, region: 'North', date: '2023-01-15' },
619
+ { id: 2, product: 'Smartphone', category: 'Electronics', sales: 800, profit: 200, region: 'South', date: '2023-01-20' },
620
+ { id: 3, product: 'Desk', category: 'Furniture', sales: 450, profit: 150, region: 'East', date: '2023-02-05' },
621
+ { id: 4, product: 'Chair', category: 'Furniture', sales: 250, profit: 75, region: 'West', date: '2023-02-10' },
622
+ { id: 5, product: 'Monitor', category: 'Electronics', sales: 350, profit: 100, region: 'North', date: '2023-03-01' },
623
+ { id: 6, product: 'Keyboard', category: 'Electronics', sales: 100, profit: 25, region: 'South', date: '2023-03-15' },
624
+ { id: 7, product: 'Mouse', category: 'Electronics', sales: 50, profit: 15, region: 'East', date: '2023-04-01' },
625
+ { id: 8, product: 'Table', category: 'Furniture', sales: 600, profit: 180, region: 'West', date: '2023-04-10' }
626
  ];
627
 
628
  // DOM elements
629
  const sidebar = document.getElementById('sidebar');
630
  const sidebarToggle = document.getElementById('sidebarToggle');
631
+ const themeToggle = document.getElementById('themeToggle');
632
  const importBtn = document.getElementById('importBtn');
633
  const browseFilesBtn = document.getElementById('browseFilesBtn');
634
  const fileInput = document.getElementById('fileInput');
 
657
  const colorSchemeSelect = document.getElementById('colorSchemeSelect');
658
  const chartTitleInput = document.getElementById('chartTitleInput');
659
  const updateChartBtn = document.getElementById('updateChartBtn');
 
 
 
 
660
  const aiSuggestBtn = document.getElementById('aiSuggestBtn');
661
  const aiRecommendationText = document.getElementById('aiRecommendationText');
662
  const applyAiRecommendationBtn = document.getElementById('applyAiRecommendationBtn');
663
+ const dismissAiRecommendationBtn = document.getElementById('dismissAiRecommendationBtn');
664
  const vizTypeBtns = document.querySelectorAll('.viz-type-btn');
665
  const dataCleanBtns = document.querySelectorAll('.data-clean-btn');
666
+ const addNewVizBtn = document.getElementById('addNewVizBtn');
667
+ const saveVizBtn = document.getElementById('saveVizBtn');
668
+ const resetVizBtn = document.getElementById('resetVizBtn');
669
+ const editDataBtn = document.getElementById('editDataBtn');
670
+ const transformDataBtn = document.getElementById('transformDataBtn');
671
+ const hideDataBtn = document.getElementById('hideDataBtn');
672
+ const recentFilesBtn = document.getElementById('recentFilesBtn');
673
+ const recentFiles = document.getElementById('recentFiles');
674
+ const templateSelect = document.getElementById('templateSelect');
675
+ const templateCards = document.querySelectorAll('.template-card');
676
+ const gridContainer = document.getElementById('gridContainer');
677
+ const saveBtn = document.getElementById('saveBtn');
678
+ const addColumnModal = document.getElementById('addColumnModal');
679
+ const closeAddColumnModalBtn = document.getElementById('closeAddColumnModalBtn');
680
+ const cancelAddColumnBtn = document.getElementById('cancelAddColumnBtn');
681
+ const confirmAddColumnBtn = document.getElementById('confirmAddColumnBtn');
682
+ const newColumnName = document.getElementById('newColumnName');
683
+ const columnFormula = document.getElementById('columnFormula');
684
+ const columnType = document.getElementById('columnType');
685
+ const filterModal = document.getElementById('filterModal');
686
+ const closeFilterModalBtn = document.getElementById('closeFilterModalBtn');
687
+ const filterColumn = document.getElementById('filterColumn');
688
+ const filterCondition = document.getElementById('filterCondition');
689
+ const filterValue = document.getElementById('filterValue');
690
+ const filterValueContainer = document.getElementById('filterValueContainer');
691
+ const cancelFilterBtn = document.getElementById('cancelFilterBtn');
692
+ const applyFilterBtn = document.getElementById('applyFilterBtn');
693
 
694
  // Current state
695
  let currentData = [];
696
+ let originalData = [];
697
  let currentVizType = 'bar';
698
+ let currentCharts = [];
699
+ let isDarkMode = false;
700
+ let nextVizId = 1;
701
 
702
  // Initialize the app
703
  function init() {
 
706
  sidebar.classList.toggle('open');
707
  });
708
 
709
+ // Toggle dark mode
710
+ themeToggle.addEventListener('click', () => {
711
+ isDarkMode = !isDarkMode;
712
+ document.body.classList.toggle('dark-mode', isDarkMode);
713
+ themeToggle.innerHTML = isDarkMode ?
714
+ '<i class="fas fa-sun text-yellow-400"></i>' :
715
+ '<i class="fas fa-moon text-gray-600"></i>';
716
+
717
+ // Update charts for dark mode
718
+ currentCharts.forEach(chart => {
719
+ if (chart.chart) {
720
+ chart.chart.options.scales.x.grid.color = isDarkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';
721
+ chart.chart.options.scales.y.grid.color = isDarkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';
722
+ chart.chart.update();
723
+ }
724
+ });
725
+ });
726
+
727
  // Import button click
728
  importBtn.addEventListener('click', () => {
729
  fileInput.click();
 
757
  btn.addEventListener('click', () => {
758
  const type = btn.dataset.type;
759
  currentVizType = type;
760
+ addVisualizationToGrid();
 
 
 
 
 
 
761
  });
762
  });
763
 
 
765
  dataCleanBtns.forEach(btn => {
766
  btn.addEventListener('click', () => {
767
  const action = btn.dataset.action;
768
+ if (action === 'add-column') {
769
+ showAddColumnModal();
770
+ } else if (action === 'filter-data') {
771
+ showFilterModal();
772
+ } else {
773
+ performDataCleaning(action);
774
+ }
775
  });
776
  });
777
 
 
786
 
787
  saveEditBtn.addEventListener('click', () => {
788
  // In a real app, we would save the edited data
789
+ saveEditedData();
790
  editDataModal.classList.add('hidden');
791
+ updateAllVisualizations();
792
+ });
793
+
794
+ // Edit data button
795
+ editDataBtn.addEventListener('click', showEditDataModal);
796
+
797
+ // Transform data button
798
+ transformDataBtn.addEventListener('click', () => {
799
+ alert('In a real app, this would show data transformation options');
800
+ });
801
+
802
+ // Hide data button
803
+ hideDataBtn.addEventListener('click', () => {
804
+ dataPreviewSection.classList.add('hidden');
805
  });
806
 
807
  // Export modal
 
818
  });
819
 
820
  confirmExportBtn.addEventListener('click', () => {
821
+ const format = document.querySelector('.export-format-btn.active')?.dataset.format || 'png';
822
+ const quality = document.getElementById('exportQuality').value;
823
+ const size = document.getElementById('exportSize').value;
824
+
825
+ exportDashboard(format, quality, size);
826
  exportModal.classList.add('hidden');
 
827
  });
828
 
829
  exportFormatBtns.forEach(btn => {
830
  btn.addEventListener('click', () => {
831
+ exportFormatBtns.forEach(b => b.classList.remove('border-blue-500', 'bg-blue-50'));
832
+ btn.classList.add('border-blue-500', 'bg-blue-50');
833
  });
834
  });
835
 
 
 
 
 
 
 
 
 
836
  // AI suggestion
837
  aiSuggestBtn.addEventListener('click', generateAiRecommendation);
838
 
839
  applyAiRecommendationBtn.addEventListener('click', applyAiRecommendation);
840
 
841
+ dismissAiRecommendationBtn.addEventListener('click', () => {
842
+ aiRecommendationSection.classList.add('hidden');
843
+ });
844
+
845
+ // Add new visualization button
846
+ addNewVizBtn.addEventListener('click', () => {
847
+ addVisualizationToGrid();
848
+ });
849
+
850
+ // Save visualization button
851
+ saveVizBtn.addEventListener('click', () => {
852
+ alert('Visualization saved! In a real app, this would persist the configuration.');
853
+ });
854
+
855
+ // Reset visualization button
856
+ resetVizBtn.addEventListener('click', () => {
857
+ if (confirm('Are you sure you want to reset all visualizations?')) {
858
+ resetVisualizations();
859
+ }
860
+ });
861
+
862
+ // Recent files button
863
+ recentFilesBtn.addEventListener('click', () => {
864
+ recentFiles.classList.toggle('hidden');
865
+ });
866
+
867
+ // Template select
868
+ templateSelect.addEventListener('change', () => {
869
+ if (templateSelect.value) {
870
+ loadTemplate(templateSelect.value);
871
+ }
872
+ });
873
+
874
+ // Template cards
875
+ templateCards.forEach(card => {
876
+ card.addEventListener('click', () => {
877
+ const template = card.dataset.template;
878
+ loadTemplate(template);
879
+ });
880
+ });
881
+
882
+ // Save dashboard button
883
+ saveBtn.addEventListener('click', saveDashboard);
884
+
885
+ // Add column modal
886
+ closeAddColumnModalBtn.addEventListener('click', () => {
887
+ addColumnModal.classList.add('hidden');
888
+ });
889
+
890
+ cancelAddColumnBtn.addEventListener('click', () => {
891
+ addColumnModal.classList.add('hidden');
892
+ });
893
+
894
+ confirmAddColumnBtn.addEventListener('click', addCalculatedColumn);
895
+
896
+ // Filter modal
897
+ closeFilterModalBtn.addEventListener('click', () => {
898
+ filterModal.classList.add('hidden');
899
+ });
900
+
901
+ cancelFilterBtn.addEventListener('click', () => {
902
+ filterModal.classList.add('hidden');
903
+ });
904
+
905
+ applyFilterBtn.addEventListener('click', applyDataFilter);
906
+
907
  // Load sample data for demo
908
  loadSampleData();
909
  }
 
911
  // Load sample data for demonstration
912
  function loadSampleData() {
913
  currentData = [...sampleData];
914
+ originalData = [...sampleData];
915
  renderDataPreview();
916
  dataPreviewSection.classList.remove('hidden');
917
  vizWorkspace.classList.remove('hidden');
918
  templatesSection.classList.remove('hidden');
919
+ updateRecentFilesList();
920
  }
921
 
922
  // Handle file selection
 
942
 
943
  if (fileType === 'csv') {
944
  // In a real app, we would parse the CSV file
945
+ alert(`CSV file "${file.name}" selected. For this demo, we'll load sample data.`);
946
+ loadSampleData();
947
+ addToRecentFiles(file.name, 'csv');
948
  } else if (fileType === 'xlsx' || fileType === 'xls') {
949
  // In a real app, we would parse the Excel file
950
+ alert(`Excel file "${file.name}" selected. For this demo, we'll load sample data.`);
951
+ loadSampleData();
952
+ addToRecentFiles(file.name, 'xlsx');
953
  } else {
954
  alert('Unsupported file type. Please upload a CSV or Excel file.');
955
  }
956
  }
957
 
958
+ // Add to recent files list
959
+ function addToRecentFiles(filename, type) {
960
+ const recentFilesList = JSON.parse(localStorage.getItem('recentFiles') || '[]');
961
+
962
+ // Remove if already exists
963
+ const existingIndex = recentFilesList.findIndex(f => f.name === filename);
964
+ if (existingIndex >= 0) {
965
+ recentFilesList.splice(existingIndex, 1);
966
+ }
967
+
968
+ // Add to beginning
969
+ recentFilesList.unshift({
970
+ name: filename,
971
+ type: type,
972
+ date: new Date().toISOString()
973
+ });
974
+
975
+ // Keep only last 5 files
976
+ if (recentFilesList.length > 5) {
977
+ recentFilesList.pop();
978
+ }
979
+
980
+ localStorage.setItem('recentFiles', JSON.stringify(recentFilesList));
981
+ updateRecentFilesList();
982
+ }
983
+
984
+ // Update recent files list in UI
985
+ function updateRecentFilesList() {
986
+ const recentFilesList = JSON.parse(localStorage.getItem('recentFiles') || '[]');
987
+ const recentFilesListEl = document.querySelector('#recentFiles ul');
988
+
989
+ if (recentFilesList.length === 0) {
990
+ recentFiles.classList.add('hidden');
991
+ return;
992
+ }
993
+
994
+ recentFilesListEl.innerHTML = '';
995
+
996
+ recentFilesList.forEach(file => {
997
+ const li = document.createElement('li');
998
+ li.className = 'flex items-center justify-between hover:bg-gray-100 p-1 rounded';
999
+
1000
+ const icon = document.createElement('i');
1001
+ icon.className = file.type === 'csv' ? 'fas fa-file-csv text-blue-500 mr-2' : 'fas fa-file-excel text-green-500 mr-2';
1002
+
1003
+ const nameSpan = document.createElement('span');
1004
+ nameSpan.textContent = file.name;
1005
+ nameSpan.className = 'truncate flex-1';
1006
+
1007
+ const dateSpan = document.createElement('span');
1008
+ dateSpan.textContent = new Date(file.date).toLocaleDateString();
1009
+ dateSpan.className = 'text-xs text-gray-500 ml-2';
1010
+
1011
+ li.appendChild(icon);
1012
+ li.appendChild(nameSpan);
1013
+ li.appendChild(dateSpan);
1014
+
1015
+ li.addEventListener('click', () => {
1016
+ alert(`Loading ${file.name}. In a real app, we would load this file.`);
1017
+ });
1018
+
1019
+ recentFilesListEl.appendChild(li);
1020
+ });
1021
+
1022
+ recentFiles.classList.remove('hidden');
1023
+ }
1024
+
1025
  // Prevent default drag and drop behaviors
1026
  function preventDefaults(e) {
1027
  e.preventDefault();
 
1071
  });
1072
  }
1073
 
1074
+ // Show edit data modal
1075
+ function showEditDataModal() {
1076
+ if (currentData.length === 0) {
1077
+ alert('No data to edit');
1078
+ return;
1079
+ }
1080
+
1081
+ // Clear existing content
1082
+ editTableHeaders.innerHTML = '';
1083
+ editTableBody.innerHTML = '';
1084
 
1085
  // Get headers from first object
1086
  const headers = Object.keys(currentData[0]);
1087
 
1088
+ // Create header row
1089
+ headers.forEach(header => {
1090
+ const th = document.createElement('th');
1091
+ th.className = 'px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider';
1092
+ th.textContent = header;
1093
+ editTableHeaders.appendChild(th);
1094
+ });
1095
 
1096
+ // Create editable data rows
1097
+ currentData.forEach((row, rowIndex) => {
1098
+ const tr = document.createElement('tr');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1099
  headers.forEach(header => {
1100
+ const td = document.createElement('td');
1101
+ td.className = 'px-6 py-2 whitespace-nowrap';
1102
+
1103
+ const input = document.createElement('input');
1104
+ input.type = 'text';
1105
+ input.className = 'w-full p-1 border border-gray-300 rounded text-sm';
1106
+ input.value = row[header];
1107
+ input.dataset.column = header;
1108
+ input.dataset.row = rowIndex;
1109
+
1110
+ td.appendChild(input);
1111
+ tr.appendChild(td);
1112
  });
1113
+ editTableBody.appendChild(tr);
1114
+ });
1115
+
1116
+ editDataModal.classList.remove('hidden');
1117
+ }
1118
+
1119
+ // Save edited data
1120
+ function saveEditedData() {
1121
+ const inputs = editTableBody.querySelectorAll('input');
1122
+
1123
+ inputs.forEach(input => {
1124
+ const rowIndex = parseInt(input.dataset.row);
1125
+ const column = input.dataset.column;
1126
+ const value = input.value;
1127
 
1128
+ // Convert to appropriate type
1129
+ let convertedValue = value;
1130
+ if (originalData[rowIndex] && typeof originalData[rowIndex][column] === 'number') {
1131
+ convertedValue = parseFloat(value) || 0;
1132
+ } else if (originalData[rowIndex] && originalData[rowIndex][column] instanceof Date) {
1133
+ convertedValue = new Date(value);
1134
+ }
 
 
 
 
 
1135
 
1136
+ currentData[rowIndex][column] = convertedValue;
1137
+ });
1138
+
1139
+ renderDataPreview();
 
 
 
 
 
 
 
1140
  }
1141
 
1142
+ // Show add column modal
1143
+ function showAddColumnModal() {
1144
+ if (currentData.length === 0) {
1145
+ alert('Please import data first');
1146
+ return;
1147
+ }
1148
 
1149
+ newColumnName.value = '';
1150
+ columnFormula.value = '';
1151
+ columnType.value = 'number';
1152
+ addColumnModal.classList.remove('hidden');
1153
+ }
1154
+
1155
+ // Add calculated column
1156
+ function addCalculatedColumn() {
1157
+ const name = newColumnName.value.trim();
1158
+ const formula = columnFormula.value.trim();
1159
+ const type = columnType.value;
1160
+
1161
+ if (!name) {
1162
+ alert('Please enter a column name');
1163
+ return;
1164
+ }
1165
+
1166
+ if (Object.keys(currentData[0]).includes(name)) {
1167
+ alert('A column with this name already exists');
1168
+ return;
1169
+ }
1170
+
1171
+ // In a real app, we would parse the formula and calculate values
1172
+ // For demo, we'll just add a column with sample values
1173
+ currentData.forEach((row, index) => {
1174
+ // Simple example formula: sales * 0.2 (20% of sales)
1175
+ let value;
1176
+ if (formula.toLowerCase().includes('sales')) {
1177
+ value = row.sales * 0.2;
1178
+ } else {
1179
+ // Default to row index if no recognizable formula
1180
+ value = index + 1;
1181
  }
1182
 
1183
+ // Convert to appropriate type
1184
+ if (type === 'number') {
1185
+ row[name] = parseFloat(value) || 0;
1186
+ } else if (type === 'text') {
1187
+ row[name] = value.toString();
1188
+ } else if (type === 'date') {
1189
+ row[name] = new Date();
1190
+ } else if (type === 'boolean') {
1191
+ row[name] = Math.random() > 0.5;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1192
  }
1193
+ });
1194
+
1195
+ addColumnModal.classList.add('hidden');
1196
+ renderDataPreview();
1197
+ updateAllVisualizations();
1198
+ }
1199
+
1200
+ // Show filter modal
1201
+ function showFilterModal() {
1202
+ if (currentData.length === 0) {
1203
+ alert('Please import data first');
1204
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1205
  }
1206
+
1207
+ // Populate column select
1208
+ filterColumn.innerHTML = '';
1209
+ const headers = Object.keys(currentData[0]);
1210
+ headers.forEach(header => {
1211
+ const option = document.createElement('option');
1212
+ option.value = header;
1213
+ option.textContent = header;
1214
+ filterColumn.appendChild(option);
1215
+ });
1216
+
1217
+ filterModal.classList.remove('hidden');
1218
  }
1219
 
1220
+ // Apply data filter
1221
+ function applyDataFilter() {
1222
+ const column = filterColumn.value;
1223
+ const condition = filterCondition.value;
1224
+ const value = filterValue.value;
 
 
 
 
 
 
 
1225
 
1226
+ if (condition !== 'empty' && condition !== 'not-empty' && !value) {
1227
+ alert('Please enter a filter value');
1228
+ return;
1229
+ }
 
1230
 
1231
+ // In a real app, we would apply the filter to the data
1232
+ // For demo, we'll just show a message
1233
+ alert(`Filter applied: ${column} ${condition} ${value}`);
 
 
1234
 
1235
+ filterModal.classList.add('hidden');
1236
+ }
1237
+
1238
+ // Perform data cleaning action
1239
+ function performDataCleaning(action) {
1240
+ if (currentData.length === 0) {
1241
+ alert('Please import data first');
1242
+ return;
1243
+ }
1244
 
1245
+ switch(action) {
1246
+ case 'remove-duplicates':
1247
+ // In a real app, we would actually remove duplicates
1248
+ const uniqueData = [];
1249
+ const seen = new Set();
1250
+
1251
+ currentData.forEach(row => {
1252
+ const key = JSON.stringify(row);
1253
+ if (!seen.has(key)) {
1254
+ seen.add(key);
1255
+ uniqueData.push(row);
1256
+ }
1257
+ });
1258
+
1259
+ currentData = uniqueData;
1260
+ alert(`${currentData.length - uniqueData.length} duplicate rows removed!`);
1261
+ break;
1262
+ case 'fill-missing':
1263
+ // In a real app, we would fill missing values
1264
+ alert('Missing values filled with defaults!');
1265
+ break;
1266
+ default:
1267
+ alert('Unknown action');
1268
  }
1269
+
1270
+ // Refresh the preview
1271
+ renderDataPreview();
1272
+ updateAllVisualizations();
1273
  }
1274
 
1275
+ // Add visualization to grid
1276
+ function addVisualizationToGrid() {
1277
+ if (currentData.length === 0) {
1278
+ alert('Please import data first');
1279
+ return;
1280
+ }
 
1281
 
1282
+ const vizId = `viz-${nextVizId++}`;
1283
+ const vizElement = document.createElement('div');
1284
+ vizElement.className = 'grid-stack-item';
1285
+ vizElement.dataset.id = vizId;
1286
+ vizElement.dataset.gsX = '0';
1287
+ vizElement.dataset.gsY = '0';
1288
+ vizElement.dataset.gsWidth = '4';
1289
+ vizElement.dataset.gsHeight = '3';
1290
 
1291
+ vizElement.innerHTML = `
1292
+ <div class="grid-stack-item-content">
1293
+ <div class="flex justify-between items-center mb-2">
1294
+ <h4 class="font-medium">New ${currentVizType} Chart</h4>
1295
+ <div class="flex space-x-1">
1296
+ <button class="viz-settings-btn p-1 text-gray-500 hover:text-blue-500" data-viz="${vizId}">
1297
+ <i class="fas fa-cog"></i>
1298
+ </button>
1299
+ <button class="viz-remove-btn p-1 text-gray-500 hover:text-red-500" data-viz="${vizId}">
1300
+ <i class="fas fa-times"></i>
1301
+ </button>
1302
+ </div>
1303
+ </div>
1304
+ <div class="chart-container relative">
1305
+ <canvas id="${vizId}-canvas"></canvas>
1306
+ </div>
1307
+ <div class="mt-2 text-xs text-gray-500">
1308
+ <span class="text-blue-500">${currentVizType}</span> chart
1309
+ </div>
1310
+ </div>
1311
+ `;
1312
 
1313
+ gridContainer.appendChild(vizElement);
 
 
 
 
1314
 
1315
+ // Initialize the chart
1316
+ const chartElement = document.getElementById(`${vizId}-canvas`);
1317
+ const chart = createChart(chartElement, currentVizType);
 
1318
 
1319
+ currentCharts.push({
1320
+ id: vizId,
1321
+ type: currentVizType,
1322
+ element: vizElement,
1323
+ chart: chart
1324
+ });
1325
 
1326
+ // Make the grid item draggable/resizable (in a real app, we'd use GridStack library)
1327
+ vizElement.addEventListener('mousedown', function(e) {
1328
+ if (e.target.classList.contains('viz-remove-btn')) {
1329
+ removeVisualization(e.target.dataset.viz);
1330
+ } else if (e.target.classList.contains('viz-settings-btn')) {
1331
+ configureVisualization(e.target.dataset.viz);
 
1332
  }
1333
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1334
 
1335
+ // Initialize grid (simplified for this demo)
1336
+ initGridStack();
1337
  }
1338
 
1339
+ // Create chart
1340
+ function createChart(canvasElement, type) {
1341
+ const headers = Object.keys(currentData[0]);
1342
+ const numericHeaders = headers.filter(h => typeof currentData[0][h] === 'number');
1343
+ const categoricalHeaders = headers.filter(h => typeof currentData[0][h] === 'string');
 
1344
 
1345
+ let xAxis = categoricalHeaders[0] || headers[0];
1346
+ let yAxis = numericHeaders[0] || headers[1] || headers[0];
1347
 
1348
+ if (type === 'pie') {
1349
+ yAxis = numericHeaders[0] || headers[0];
1350
+ } else if (type === 'scatter') {
1351
+ xAxis = numericHeaders[0] || headers[0];
1352
+ yAxis = numericHeaders[1] || headers[1] || headers[0];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1353
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
1354
 
1355
+ const labels = currentData.map(item => item[xAxis]);
1356
+ const data = currentData.map(item => item[yAxis]);
 
 
 
1357
 
1358
+ const backgroundColors = getChartColors(data.length, 'default');
 
 
 
 
1359
 
1360
+ const config = {
1361
+ type: type === 'pie' ? 'pie' :
1362
+ type === 'bar' ? 'bar' :
1363
+ type === 'line' ? 'line' :
1364
+ type === 'scatter' ? 'scatter' : 'bar',
1365
+ data: {
1366
+ labels: labels,
1367
+ datasets: [{
1368
+ label: yAxis,
1369
+ data: data,
1370
+ backgroundColor: backgroundColors,
1371
+ borderColor: type === 'pie' ? backgroundColors : '#3b82f6',
1372
+ borderWidth: 1
1373
+ }]
1374
+ },
1375
+ options: {
1376
+ responsive: true,
1377
+ maintainAspectRatio: false,
1378
+ plugins: {
1379
+ title: {
1380
+ display: true,
1381
+ text: `${type} Chart: ${xAxis} vs ${yAxis}`,
1382
+ font: {
1383
+ size: 14
1384
+ }
1385
+ },
1386
+ legend: {
1387
+ position: type === 'pie' ? 'right' : 'top',
1388
+ }
1389
+ },
1390
+ scales: type === 'pie' ? {} : {
1391
+ x: {
1392
+ grid: {
1393
+ color: isDarkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'
1394
+ }
1395
+ },
1396
+ y: {
1397
+ grid: {
1398
+ color: isDarkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'
1399
+ }
1400
+ }
1401
+ }
1402
+ }
1403
+ };
1404
 
1405
+ return new Chart(canvasElement, config);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1406
  }
1407
 
1408
+ // Get chart colors based on scheme
1409
+ function getChartColors(count, scheme) {
 
 
1410
  const schemes = {
1411
  default: ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6'],
1412
  rainbow: ['#ff0000', '#ff7f00', '#ffff00', '#00ff00', '#0000ff', '#4b0082', '#9400d3'],
 
1417
  };
1418
 
1419
  const colors = schemes[scheme] || schemes.default;
1420
+ const result = [];
1421
+
1422
+ for (let i = 0; i < count; i++) {
1423
+ result.push(colors[i % colors.length]);
1424
+ }
1425
+
1426
+ return result;
1427
+ }
1428
+
1429
+ // Initialize grid stack (simplified for this demo)
1430
+ function initGridStack() {
1431
+ // In a real app, we would use GridStack library for proper grid functionality
1432
+ const items = gridContainer.querySelectorAll('.grid-stack-item');
1433
+
1434
+ items.forEach(item => {
1435
+ item.style.position = 'absolute';
1436
+ item.style.left = `${item.dataset.gsX * 25}%`;
1437
+ item.style.top = `${item.dataset.gsY * 100}px`;
1438
+ item.style.width = `${item.dataset.gsWidth * 25}%`;
1439
+ item.style.height = `${item.dataset.gsHeight * 100}px`;
1440
+
1441
+ // Make draggable (simplified)
1442
+ item.draggable = true;
1443
+ item.addEventListener('dragstart', handleDragStart);
1444
+ item.addEventListener('dragend', handleDragEnd);
1445
+ });
1446
+
1447
+ gridContainer.addEventListener('dragover', handleDragOver);
1448
+ gridContainer.addEventListener('drop', handleDropItem);
1449
+ }
1450
+
1451
+ // Drag and drop handlers for grid items (simplified)
1452
+ function handleDragStart(e) {
1453
+ e.dataTransfer.setData('text/plain', e.target.dataset.id);
1454
+ e.target.style.opacity = '0.5';
1455
+ }
1456
+
1457
+ function handleDragEnd(e) {
1458
+ e.target.style.opacity = '1';
1459
+ }
1460
+
1461
+ function handleDragOver(e) {
1462
+ e.preventDefault();
1463
+ }
1464
+
1465
+ function handleDropItem(e) {
1466
+ e.preventDefault();
1467
+ const id = e.dataTransfer.getData('text/plain');
1468
+ const item = document.querySelector(`[data-id="${id}"]`);
1469
+
1470
+ if (item && e.target === gridContainer) {
1471
+ const rect = gridContainer.getBoundingClientRect();
1472
+ const x = e.clientX - rect.left;
1473
+ const y = e.clientY - rect.top;
1474
+
1475
+ // Calculate new position (simplified)
1476
+ const col = Math.floor(x / (rect.width / 4));
1477
+ const row = Math.floor(y / 100);
1478
+
1479
+ item.style.left = `${col * 25}%`;
1480
+ item.style.top = `${row * 100}px`;
1481
+ item.dataset.gsX = col;
1482
+ item.dataset.gsY = row;
1483
+ }
1484
+ }
1485
+
1486
+ // Remove visualization
1487
+ function removeVisualization(id) {
1488
+ if (confirm('Are you sure you want to remove this visualization?')) {
1489
+ const index = currentCharts.findIndex(chart => chart.id === id);
1490
+ if (index >= 0) {
1491
+ currentCharts[index].chart.destroy();
1492
+ currentCharts[index].element.remove();
1493
+ currentCharts.splice(index, 1);
1494
+ }
1495
+ }
1496
+ }
1497
+
1498
+ // Configure visualization
1499
+ function configureVisualization(id) {
1500
+ const chartConfig = currentCharts.find(chart => chart.id === id);
1501
+ if (chartConfig) {
1502
+ alert(`In a real app, this would show configuration options for ${chartConfig.type} chart`);
1503
+ }
1504
+ }
1505
+
1506
+ // Update all visualizations when data changes
1507
+ function updateAllVisualizations() {
1508
+ currentCharts.forEach(chartConfig => {
1509
+ updateVisualization(chartConfig);
1510
+ });
1511
+ }
1512
+
1513
+ // Update a specific visualization
1514
+ function updateVisualization(chartConfig) {
1515
+ const headers = Object.keys(currentData[0]);
1516
+ const numericHeaders = headers.filter(h => typeof currentData[0][h] === 'number');
1517
+ const categoricalHeaders = headers.filter(h => typeof currentData[0][h] === 'string');
1518
+
1519
+ let xAxis = categoricalHeaders[0] || headers[0];
1520
+ let yAxis = numericHeaders[0] || headers[1] || headers[0];
1521
+
1522
+ if (chartConfig.type === 'pie') {
1523
+ yAxis = numericHeaders[0] || headers[0];
1524
+ } else if (chartConfig.type === 'scatter') {
1525
+ xAxis = numericHeaders[0] || headers[0];
1526
+ yAxis = numericHeaders[1] || headers[1] || headers[0];
1527
+ }
1528
+
1529
+ const labels = currentData.map(item => item[xAxis]);
1530
+ const data = currentData.map(item => item[yAxis]);
1531
+
1532
+ chartConfig.chart.data.labels = labels;
1533
+ chartConfig.chart.data.datasets[0].data = data;
1534
+ chartConfig.chart.data.datasets[0].label = yAxis;
1535
+ chartConfig.chart.options.plugins.title.text = `${chartConfig.type} Chart: ${xAxis} vs ${yAxis}`;
1536
+ chartConfig.chart.update();
1537
+ }
1538
+
1539
+ // Reset visualizations
1540
+ function resetVisualizations() {
1541
+ gridContainer.innerHTML = '';
1542
+ currentCharts.forEach(chart => chart.chart.destroy());
1543
+ currentCharts = [];
1544
+ nextVizId = 1;
1545
  }
1546
 
1547
  // Generate AI recommendation
 
1556
  const headers = Object.keys(currentData[0]);
1557
  const numericHeaders = headers.filter(h => typeof currentData[0][h] === 'number');
1558
  const categoricalHeaders = headers.filter(h => typeof currentData[0][h] === 'string');
1559
+ const dateHeaders = headers.filter(h => !isNaN(Date.parse(currentData[0][h])));
1560
 
1561
  let recommendation = '';
1562
 
1563
+ if (dateHeaders.length > 0 && numericHeaders.length > 0) {
1564
+ recommendation = `A line chart showing ${numericHeaders[0]} over time would effectively visualize trends in your data.`;
1565
+ currentVizType = 'line';
1566
+ }
1567
+ else if (numericHeaders.length >= 2 && categoricalHeaders.length >= 1) {
1568
  recommendation = `A grouped bar chart comparing ${numericHeaders[0]} and ${numericHeaders[1]} across different ${categoricalHeaders[0]} categories would effectively show the relationships in your data.`;
1569
+ currentVizType = 'bar';
1570
+ }
1571
+ else if (numericHeaders.length >= 1 && categoricalHeaders.length >= 1) {
1572
  recommendation = `A pie chart showing the distribution of ${numericHeaders[0]} by ${categoricalHeaders[0]} would be a good choice to visualize proportions.`;
1573
+ currentVizType = 'pie';
1574
+ }
1575
+ else if (numericHeaders.length >= 2) {
1576
  recommendation = `A scatter plot with ${numericHeaders[0]} on the X-axis and ${numericHeaders[1]} on the Y-axis would help identify correlations.`;
1577
+ currentVizType = 'scatter';
1578
+ }
1579
+ else {
1580
  recommendation = `Based on your data structure, a simple table might be the most effective way to present this information.`;
1581
  }
1582
 
 
1586
 
1587
  // Apply AI recommendation
1588
  function applyAiRecommendation() {
1589
+ addVisualizationToGrid();
1590
+ aiRecommendationSection.classList.add('hidden');
 
 
 
 
 
 
 
1591
  }
1592
 
1593
+ // Load template
1594
+ function loadTemplate(template) {
1595
+ if (template === 'sales') {
1596
+ // In a real app, this would configure sales-specific visualizations
1597
+ resetVisualizations();
1598
+ currentVizType = 'bar';
1599
+ addVisualizationToGrid();
1600
+ currentVizType = 'line';
1601
+ addVisualizationToGrid();
1602
+ alert('Sales dashboard template loaded with sample visualizations');
1603
+ }
1604
+ else if (template === 'customer') {
1605
+ resetVisualizations();
1606
+ currentVizType = 'pie';
1607
+ addVisualizationToGrid();
1608
+ currentVizType = 'bar';
1609
+ addVisualizationToGrid();
1610
+ alert('Customer analytics template loaded with sample visualizations');
1611
+ }
1612
+ else if (template === 'process') {
1613
+ resetVisualizations();
1614
+ currentVizType = 'flowchart';
1615
+ addVisualizationToGrid();
1616
+ alert('Process flow template loaded with sample visualization');
 
 
 
 
 
 
1617
  }
1618
  }
1619
 
1620
+ // Export dashboard
1621
+ function exportDashboard(format, quality, size) {
1622
+ // In a real app, we would generate the export file
1623
+ alert(`Exporting dashboard as ${format} (${quality}, ${size})`);
1624
+ }
1625
+
1626
+ // Save dashboard
1627
+ function saveDashboard() {
1628
+ const dashboardConfig = {
1629
+ visualizations: currentCharts.map(chart => ({
1630
+ id: chart.id,
1631
+ type: chart
1632
  </html>
prompts.txt CHANGED
@@ -1 +1,2 @@
1
- Hello, I would like to create a data visualization dashboard web app. Here are the key functionalities I need: Allow users to import data from CSV or Excel files. After importing, allow users to select what type of visualization they want to create: Charts (bar, line, pie, scatter, etc.) Diagrams (flowcharts, organizational charts, etc.) Mind Maps Enable drag-and-drop interface for building and customizing visualizations. Provide basic data cleaning options (e.g., remove duplicates, fill missing values). Allow users to filter, sort, and group data before visualization. Enable dynamic updating: if the data changes, the visualization updates automatically. Allow users to customize the visuals (colors, labels, titles, sizes, etc.). Support saving and exporting visualizations (as images or PDFs). Provide dashboard templates for faster setup. Offer an AI assistant suggestion feature: recommend the best type of visualization based on the dataset. Support multi-language UI (optional, but nice to have). The dashboard should have a clean, responsive design that works on desktop and mobile.
 
 
1
+ Hello, I would like to create a data visualization dashboard web app. Here are the key functionalities I need: Allow users to import data from CSV or Excel files. After importing, allow users to select what type of visualization they want to create: Charts (bar, line, pie, scatter, etc.) Diagrams (flowcharts, organizational charts, etc.) Mind Maps Enable drag-and-drop interface for building and customizing visualizations. Provide basic data cleaning options (e.g., remove duplicates, fill missing values). Allow users to filter, sort, and group data before visualization. Enable dynamic updating: if the data changes, the visualization updates automatically. Allow users to customize the visuals (colors, labels, titles, sizes, etc.). Support saving and exporting visualizations (as images or PDFs). Provide dashboard templates for faster setup. Offer an AI assistant suggestion feature: recommend the best type of visualization based on the dataset. Support multi-language UI (optional, but nice to have). The dashboard should have a clean, responsive design that works on desktop and mobile.
2
+ Hello, I would like to create a fully functional data visualization dashboard, keeping a clean and modern UI like the attached reference. Here’s exactly what I need: Core Functionalities: Data Import: Drag-and-drop + browse to upload files. Support CSV and Excel (XLS, XLSX) formats. Validate uploaded file (format errors, missing values). Data Preview: Show imported data in a clean table. Allow basic transformations: Edit cells manually. Filter columns (e.g., hide/show). Sort and search. Add simple calculated columns (optional). Transform data (change column types: number, text, date). Visualization Workspace: Let users select: Chart type (Bar, Line, Pie, Scatter, Flowchart, Mind Map). X-axis and Y-axis fields dynamically. Color scheme (default options, light/dark themes). Chart title and labels. When user clicks "Update Visualization," render the chart instantly based on selected settings. Dashboard Templates: Offer ready-made templates like: Sales Dashboard Customer Analytics Process Flow Users can load a template to auto-configure charts based on typical datasets. Save and Export: Allow saving dashboards in the browser (local storage first, option for cloud later). Export visualizations as PNG, PDF, or CSV. Enhancements to Make It Great: AI Suggestions Panel: Recommend the best chart type based on the dataset (e.g., large numbers -> bar chart, time series -> line chart). Multi-visualization Dashboard: Let users create multiple charts and arrange them freely (drag-and-drop layout). Live Updates: If data is edited, visualization updates live without needing to press "update." Responsive Design: Fully optimized for desktop, tablet, and mobile. Help Tooltip System: Small "?" icons near each feature for quick help pop-ups. Recent Files and Auto-save: Show a list of recently uploaded files. Auto-save the latest dashboard state locally. Important Notes: Prioritize fast performance even with large files (optimize loading and rendering). Ensure the dashboard is user-friendly and no-code — users should not need technical skills to use it.