Skip to content

Commit ee97121

Browse files
Upload data page implemented for the frontend (#4)
Co-authored-by: Shalinga Manasinghe <shalingams@gmail.com>
1 parent 58bdeac commit ee97121

29 files changed

+2114
-259
lines changed

babel.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
presets: [
3+
["@babel/preset-env", { targets: { node: "current" } }],
4+
"@babel/preset-react",
5+
],
6+
};

frontend/src/App.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import UnifiedHeader from './components/UnifiedHeader';
66
import FrontPage from './components/FrontPage';
77
import ChatPanel from './components/ChatPanel';
88
import RightPanelTabs from './components/RightPanelTabs';
9+
import { BrowserRouter, Routes, Route } from 'react-router-dom';
10+
import DataSourceContextPage from './pages/DataSourceContextPage';
911

10-
function App() {
12+
function MainApp() {
1113
const [started, setStarted] = useState(false);
1214
const [initialUserMessage, setInitialUserMessage] = useState('');
1315
const [queries, setQueries] = useState([]);
@@ -41,8 +43,7 @@ function App() {
4143
};
4244

4345
return (
44-
<ThemeProvider theme={theme}>
45-
<CssBaseline />
46+
<>
4647
<UnifiedHeader
4748
selectedTab={selectedTab}
4849
onTabChange={handleTabChange}
@@ -99,8 +100,22 @@ function App() {
99100
</Box>
100101
</Box>
101102
)}
103+
</>
104+
);
105+
}
106+
107+
function AppWrapper() {
108+
return (
109+
<ThemeProvider theme={theme}>
110+
<CssBaseline />
111+
<BrowserRouter>
112+
<Routes>
113+
<Route path="/" element={<MainApp />} />
114+
<Route path="/settings" element={<DataSourceContextPage />} />
115+
</Routes>
116+
</BrowserRouter>
102117
</ThemeProvider>
103118
);
104119
}
105120

106-
export default App;
121+
export default AppWrapper;

frontend/src/api/dataSources.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
* dataSources.js
3+
* API utility functions for managing data sources (files and databases).
4+
*
5+
* Functions:
6+
* - uploadDataSourceFile: Upload CSV/Excel file as data source
7+
* - addDatabaseSource: Add database connection as data source
8+
* - fetchDataSources: Fetch all data sources
9+
* - updateDataSource: Update a data source by id
10+
* - deleteDataSource: Delete a data source by id
11+
*/
12+
13+
/**
14+
* Upload CSV/Excel file as data source
15+
* @param {File} file - The file to upload
16+
* @returns {Promise<Object>} The new data source object
17+
*/
18+
export async function uploadDataSourceFile(file) {
19+
const formData = new FormData();
20+
formData.append('file', file);
21+
const res = await fetch('/api/data-sources/files', {
22+
method: 'POST',
23+
body: formData
24+
});
25+
if (!res.ok) throw new Error('Failed to upload data source file');
26+
return await res.json();
27+
}
28+
29+
/**
30+
* Add database connection as data source
31+
* @param {Object} data - Database connection details
32+
* @returns {Promise<Object>} The new data source object
33+
*/
34+
export async function addDatabaseSource(data) {
35+
const res = await fetch('/api/data-sources/databases', {
36+
method: 'POST',
37+
headers: { 'Content-Type': 'application/json' },
38+
body: JSON.stringify(data)
39+
});
40+
if (!res.ok) throw new Error('Failed to add database source');
41+
return await res.json();
42+
}
43+
44+
/**
45+
* Fetch all data sources
46+
* @returns {Promise<Array>} List of data sources
47+
*/
48+
export async function fetchDataSources() {
49+
const res = await fetch('/api/data-sources');
50+
if (!res.ok) throw new Error('Failed to fetch data sources');
51+
return await res.json();
52+
}
53+
54+
/**
55+
* Update a data source by id
56+
* @param {string|number} id - Data source id
57+
* @param {Object} data - Data to update
58+
* @returns {Promise<Object>} The updated data source object
59+
*/
60+
export async function updateDataSource(id, data) {
61+
const res = await fetch(`/api/data-sources/${id}`, {
62+
method: 'PUT',
63+
headers: { 'Content-Type': 'application/json' },
64+
body: JSON.stringify(data)
65+
});
66+
if (!res.ok) throw new Error('Failed to update data source');
67+
return await res.json();
68+
}
69+
70+
/**
71+
* Delete a data source by id
72+
* @param {string|number} id - Data source id
73+
* @returns {Promise<Object>} The deleted data source object
74+
*/
75+
export async function deleteDataSource(id) {
76+
const res = await fetch(`/api/data-sources/${id}`, {
77+
method: 'DELETE'
78+
});
79+
if (!res.ok) throw new Error('Failed to delete data source');
80+
return await res.json();
81+
}

frontend/src/api/llmContext.js

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/**
2+
* llmContext.js
3+
* API utility functions for managing LLM context files, domain knowledge, and templates.
4+
*
5+
* Functions:
6+
* - fetchContextFiles: Fetch all context files
7+
* - uploadContextFile: Upload a new context file
8+
* - deleteContextFile: Delete a context file by id
9+
* - fetchDomainKnowledge: Fetch domain knowledge text
10+
* - updateDomainKnowledge: Update domain knowledge text
11+
* - fetchTemplates: Fetch available templates
12+
* - applyTemplate: Apply a template to domain knowledge
13+
*/
14+
15+
/**
16+
* Fetch all context files
17+
* @returns {Promise<Array>} List of context files
18+
*/
19+
export async function fetchContextFiles() {
20+
const res = await fetch('/api/llm-context/files');
21+
if (!res.ok) throw new Error('Failed to fetch context files');
22+
return await res.json();
23+
}
24+
25+
/**
26+
* Upload a new context file
27+
* @param {Object} data - File data to upload
28+
* @returns {Promise<Object>} The new context file object
29+
*/
30+
export async function uploadContextFile(data) {
31+
const res = await fetch('/api/llm-context/files', {
32+
method: 'POST',
33+
headers: { 'Content-Type': 'application/json' },
34+
body: JSON.stringify(data)
35+
});
36+
if (!res.ok) throw new Error('Failed to upload context file');
37+
return await res.json();
38+
}
39+
40+
/**
41+
* Delete a context file by id
42+
* @param {string|number} id - Context file id
43+
* @returns {Promise<Object>} The deleted context file object
44+
*/
45+
export async function deleteContextFile(id) {
46+
const res = await fetch(`/api/llm-context/files/${id}`, {
47+
method: 'DELETE'
48+
});
49+
if (!res.ok) throw new Error('Failed to delete context file');
50+
return await res.json();
51+
}
52+
53+
/**
54+
* Fetch domain knowledge text
55+
* @returns {Promise<string>} Domain knowledge text
56+
*/
57+
export async function fetchDomainKnowledge() {
58+
const res = await fetch('/api/llm-context/domain');
59+
if (!res.ok) throw new Error('Failed to fetch domain knowledge');
60+
return await res.text();
61+
}
62+
63+
/**
64+
* Update domain knowledge text
65+
* @param {Object} data - Domain knowledge data
66+
* @returns {Promise<string>} Updated domain knowledge text
67+
*/
68+
export async function updateDomainKnowledge(data) {
69+
const res = await fetch('/api/llm-context/domain', {
70+
method: 'POST',
71+
headers: { 'Content-Type': 'application/json' },
72+
body: JSON.stringify(data)
73+
});
74+
if (!res.ok) throw new Error('Failed to update domain knowledge');
75+
return await res.text();
76+
}
77+
78+
/**
79+
* Fetch available templates
80+
* @returns {Promise<Array>} List of templates
81+
*/
82+
export async function fetchTemplates() {
83+
const res = await fetch('/api/llm-context/templates');
84+
if (!res.ok) throw new Error('Failed to fetch templates');
85+
return await res.json();
86+
}
87+
88+
/**
89+
* Apply a template to domain knowledge
90+
* @param {Object} data - Template data
91+
* @returns {Promise<string>} Resulting domain knowledge text
92+
*/
93+
export async function applyTemplate(data) {
94+
const res = await fetch('/api/llm-context/templates/apply', {
95+
method: 'POST',
96+
headers: { 'Content-Type': 'application/json' },
97+
body: JSON.stringify(data)
98+
});
99+
if (!res.ok) throw new Error('Failed to apply template');
100+
return await res.text();
101+
}

frontend/src/components/AddChartDialog.js

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
// src/components/AddChartDialog.js
1+
/**
2+
* AddChartDialog
3+
* Dialog for creating a new chart/visualization. Lets user select chart type, data source, axes, and measure(s).
4+
*
5+
* Props:
6+
* open (bool): Whether the dialog is open
7+
* onClose (func): Callback to close dialog
8+
* onAddChart (func): Callback to add chart, receives chart config
9+
* dataSources (object): Map of data source names to data arrays
10+
*/
211
import React, { useState } from 'react';
312
import {
413
Dialog,
@@ -18,27 +27,27 @@ import {
1827
ListItemText,
1928
} from '@mui/material';
2029
import BarChartIcon from '@mui/icons-material/BarChart';
21-
import ShowChartIcon from '@mui/icons-material/ShowChart'; // line
22-
import TimelineIcon from '@mui/icons-material/Timeline'; // area
30+
import ShowChartIcon from '@mui/icons-material/ShowChart';
31+
import TimelineIcon from '@mui/icons-material/Timeline';
2332
import PieChartIcon from '@mui/icons-material/PieChart';
24-
import ActivityIcon from '@mui/icons-material/AutoAwesomeMotion'; // or similar for KPI
33+
import ActivityIcon from '@mui/icons-material/AutoAwesomeMotion';
2534

2635
function AddChartDialog({
2736
open,
2837
onClose,
2938
onAddChart,
30-
dataSources, // e.g. Object with data arrays
31-
queries, // e.g. for advanced referencing
39+
dataSources,
3240
}) {
41+
// State for form fields
3342
const [title, setTitle] = useState('');
3443
const [chartType, setChartType] = useState('');
3544
const [dataSource, setDataSource] = useState('');
3645
const [xAxis, setXAxis] = useState('');
3746
const [yAxes, setYAxes] = useState([]); // multi-select
3847
const [errorMsg, setErrorMsg] = useState('');
3948

49+
// Reset all fields and close dialog
4050
const handleClose = () => {
41-
// Reset
4251
setTitle('');
4352
setChartType('');
4453
setDataSource('');
@@ -48,9 +57,8 @@ function AddChartDialog({
4857
onClose();
4958
};
5059

51-
// Attempt to create a new chart
60+
// Validate and submit chart config
5261
const handleAddChart = () => {
53-
// Basic validation
5462
if (!title.trim() || !chartType || !dataSource) {
5563
setErrorMsg('Please fill all required fields');
5664
return;
@@ -63,25 +71,23 @@ function AddChartDialog({
6371
setErrorMsg('Please select at least one Y axis/measure');
6472
return;
6573
}
66-
67-
// Build the new chart config
6874
onAddChart({
6975
id: `chart-${Date.now()}`,
7076
title,
7177
chartType,
7278
dataSource,
7379
xKey: xAxis,
74-
yKeys: yAxes, // multi-series
80+
yKeys: yAxes,
7581
});
7682
handleClose();
7783
};
7884

79-
// Build the array of columns for the selected data source
85+
// Get columns for selected data source
8086
const columns = dataSource && dataSources[dataSource] && dataSources[dataSource].length > 0
8187
? Object.keys(dataSources[dataSource][0])
8288
: [];
8389

84-
// For multi-select Y axis
90+
// Handle Y axis selection (multi-select)
8591
const handleYAxesChange = (e) => {
8692
const value = e.target.value;
8793
setYAxes(typeof value === 'string' ? value.split(',') : value);
@@ -98,6 +104,7 @@ function AddChartDialog({
98104
fullWidth
99105
value={title}
100106
onChange={(e) => setTitle(e.target.value)}
107+
inputProps={{ 'aria-label': 'Chart title' }}
101108
/>
102109

103110
<Typography variant="body2" sx={{ fontWeight: 600 }}>
@@ -107,30 +114,35 @@ function AddChartDialog({
107114
<IconButton
108115
color={chartType === 'bar' ? 'primary' : 'default'}
109116
onClick={() => setChartType('bar')}
117+
aria-label="Bar chart"
110118
>
111119
<BarChartIcon />
112120
</IconButton>
113121
<IconButton
114122
color={chartType === 'line' ? 'primary' : 'default'}
115123
onClick={() => setChartType('line')}
124+
aria-label="Line chart"
116125
>
117126
<ShowChartIcon />
118127
</IconButton>
119128
<IconButton
120129
color={chartType === 'area' ? 'primary' : 'default'}
121130
onClick={() => setChartType('area')}
131+
aria-label="Area chart"
122132
>
123133
<TimelineIcon />
124134
</IconButton>
125135
<IconButton
126136
color={chartType === 'pie' ? 'primary' : 'default'}
127137
onClick={() => setChartType('pie')}
138+
aria-label="Pie chart"
128139
>
129140
<PieChartIcon />
130141
</IconButton>
131142
<IconButton
132143
color={chartType === 'kpi' ? 'primary' : 'default'}
133144
onClick={() => setChartType('kpi')}
145+
aria-label="KPI"
134146
>
135147
<ActivityIcon />
136148
</IconButton>
@@ -142,6 +154,7 @@ function AddChartDialog({
142154
value={dataSource}
143155
label="Data Source"
144156
onChange={(e) => setDataSource(e.target.value)}
157+
inputProps={{ 'aria-label': 'Data source' }}
145158
>
146159
{Object.keys(dataSources).map((ds) => (
147160
<MenuItem key={ds} value={ds}>
@@ -158,6 +171,7 @@ function AddChartDialog({
158171
value={xAxis}
159172
label="X Axis"
160173
onChange={(e) => setXAxis(e.target.value)}
174+
inputProps={{ 'aria-label': 'X axis' }}
161175
>
162176
{columns.map((col) => (
163177
<MenuItem key={col} value={col}>
@@ -180,6 +194,7 @@ function AddChartDialog({
180194
label="Y Axis"
181195
onChange={handleYAxesChange}
182196
renderValue={(selected) => (Array.isArray(selected) ? selected.join(', ') : selected)}
197+
inputProps={{ 'aria-label': 'Y axis' }}
183198
>
184199
{columns.map((col) => (
185200
<MenuItem key={col} value={col}>

0 commit comments

Comments
 (0)