diff --git a/Homework2/Dashboard.png b/Homework2/Dashboard.png
new file mode 100644
index 0000000..41c2f54
Binary files /dev/null and b/Homework2/Dashboard.png differ
diff --git a/Homework2/vkosuri/React-Template/README.md b/Homework2/vkosuri/React-Template/README.md
new file mode 100644
index 0000000..765052f
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/README.md
@@ -0,0 +1,26 @@
+# More about the Framework
+
+
+This is a template in React and TypeScript. React has a steeper learning curve than Vue.js because it requires understanding JSX syntax and concepts like hooks and component lifecycle.
+
+If you want to use React but not with TypeScript, just remove any type specifications from the `Example.tsx`, `Notes.tsx`, and `NotesWithReducer.tsx`. You can always refer to `VanillaJS-Template/example.js` for this migration.
+
+
+## Files You Have to Care about
+
+`package.json` is where we manage the libraries we installed. Besides this, most of the files you can ignore, but **the files under `./src/` are your concern**.
+
+* `./src/main.tsx` is the root script file for React that instatinates our single page application.
+* `./src/App.tsx` is the root file for all **development** needs and is also where we manage the layout and load in components.
+* `./src/types.ts` is usually where we declare our customized types if you're planning to use it.
+* `./src/stores/` is where we manage the stores if you're planning to use it. The store is responsible for global state management.
+* `./src/components/` is where we create the components. You may have multiple components depends on your design.
+ * `Example.tsx` shows how to read `.csv` and `.json`, how component size is being watched, how a bar chart is created, and how the component updates if there are any changes.
+ * `Notes.tsx` shows the difference of **state** and **prop**, how to use MUI, and how a local state updates based on interaction.
+ * `NotesWithReducer.tsx` is equivalent to `Notes.tsx`, excepts it uses store called reducer.
+
+## Libraries Installed in this Framework
+ * D3.js v7 for visualization
+ * [axios](https://axios-http.com/docs/intro) for API.
+ * [Material UI](https://mui.com/material-ui/getting-started/) for UI components.
+ * [lodash](https://lodash.com/) for utility functions in JavaScript.
diff --git a/Homework2/vkosuri/React-Template/data/Student Mental health.csv b/Homework2/vkosuri/React-Template/data/Student Mental health.csv
new file mode 100644
index 0000000..0d75ad4
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/data/Student Mental health.csv
@@ -0,0 +1,102 @@
+Timestamp,Choose your gender,Age,What is your course?,Your current year of Study,What is your CGPA?,Marital status,Do you have Depression?,Do you have Anxiety?,Do you have Panic attack?,Did you seek any specialist for a treatment?
+8/7/2020 12:02,Female,18,Engineering,year 1,3.00 - 3.49,No,Yes,No,Yes,No
+8/7/2020 12:04,Male,21,Islamic education,year 2,3.00 - 3.49,No,No,Yes,No,No
+8/7/2020 12:05,Male,19,BIT,Year 1,3.00 - 3.49,No,Yes,Yes,Yes,No
+8/7/2020 12:06,Female,22,Laws,year 3,3.00 - 3.49,Yes,Yes,No,No,No
+8/7/2020 12:13,Male,23,Mathemathics,year 4,3.00 - 3.49,No,No,No,No,No
+8/7/2020 12:31,Male,19,Engineering,Year 2,3.50 - 4.00,No,No,No,Yes,No
+8/7/2020 12:32,Female,23,Pendidikan islam,year 2,3.50 - 4.00 ,Yes,Yes,No,Yes,No
+8/7/2020 12:33,Female,18,BCS,year 1,3.50 - 4.00,No,No,Yes,No,No
+8/7/2020 12:35,Female,19,Human Resources,Year 2,2.50 - 2.99,No,No,No,No,No
+8/7/2020 12:39,Male,18,Irkhs,year 1,3.50 - 4.00,No,No,Yes,Yes,No
+8/7/2020 12:39,Female,20,Psychology,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 12:39,Female,24,Engineering,Year 3,3.50 - 4.00,Yes,Yes,No,No,No
+8/7/2020 12:40,Female,18,BCS,year 1,3.00 - 3.49,No,Yes,No,No,No
+8/7/2020 12:41,Male,19,Engineering,year 1,3.00 - 3.49,No,No,No,No,No
+8/7/2020 12:43,Female,18,KENMS,Year 2,3.50 - 4.00,No,No,Yes,No,No
+8/7/2020 12:43,Male,24,BCS,Year 3,3.50 - 4.00,No,No,No,No,No
+8/7/2020 12:46,Female,24,Accounting ,year 3,3.00 - 3.49,No,No,No,No,No
+8/7/2020 12:52,Female,24,ENM,year 4,3.00 - 3.49,Yes,Yes,Yes,Yes,No
+8/7/2020 13:05,Female,20,BIT,Year 2,3.50 - 4.00,No,No,Yes,No,No
+8/7/2020 13:07,Female,18,Marine science,year 2,3.50 - 4.00,Yes,Yes,Yes,Yes,No
+8/7/2020 13:12,Female,19,Engineering,year 1,3.00 - 3.49,No,No,No,Yes,No
+8/7/2020 13:13,Female,18,KOE,Year 2,3.00 - 3.49,No,No,No,No,No
+8/7/2020 13:13,Female,24,BCS,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 13:15,Female,24,Engineering,year 1,3.00 - 3.49,No,No,No,No,No
+8/7/2020 13:17,Female,23,BCS,Year 3,3.50 - 4.00,No,Yes,Yes,Yes,No
+8/7/2020 13:29,Female,18,Banking Studies,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 13:35,Female,19,Engineering,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 13:41,Male,18,Engineering,Year 2,3.00 - 3.49,Yes,Yes,Yes,No,No
+8/7/2020 13:58,Female,24,BIT,Year 3,3.50 - 4.00,Yes,Yes,Yes,Yes,Yes
+8/7/2020 14:05,Female,24,BCS,year 4,3.50 - 4.00,No,No,No,No,No
+8/7/2020 14:27,Female,23,Business Administration,Year 2,3.00 - 3.49,No,No,No,No,No
+8/7/2020 14:29,Male,18,BCS,year 2,3.00 - 3.49,No,No,No,No,No
+8/7/2020 14:29,Male,19,BCS,year 1,3.50 - 4.00,No,No,No,Yes,No
+8/7/2020 14:31,Male,18,BCS,Year 2,3.50 - 4.00,Yes,Yes,Yes,No,Yes
+8/7/2020 14:41,Female,19,BIT,year 1,3.00 - 3.49,No,Yes,Yes,Yes,No
+8/7/2020 14:43,Female,18,Engineering,year 1,2.00 - 2.49,No,No,No,No,No
+8/7/2020 14:43,Female,18,Law,Year 3,3.00 - 3.49,No,Yes,Yes,No,No
+8/7/2020 14:45,Female,19,BIT,year 1,2.50 - 2.99,No,Yes,Yes,Yes,No
+8/7/2020 14:47,Female,18,KIRKHS,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 14:56,Female,24,Engineering,Year 2,2.50 - 2.99,Yes,Yes,No,Yes,Yes
+8/7/2020 14:57,Female,24,BIT,Year 3,3.00 - 3.49,No,No,Yes,No,No
+8/7/2020 14:57,Female,22,Engineering,year 4,3.50 - 4.00,No,No,No,No,No
+8/7/2020 14:58,Female,20,Usuluddin ,year 2,3.00 - 3.49,No,Yes,No,No,No
+8/7/2020 15:07,Male,,BIT,year 1,0 - 1.99,No,No,No,No,No
+8/7/2020 15:08,Male,23,TAASL,year 2,3.50 - 4.00,No,No,No,Yes,No
+8/7/2020 15:09,Male,18,BCS,year 1,3.50 - 4.00,No,No,Yes,Yes,No
+8/7/2020 15:12,Female,19,Engineering,year 1,3.50 - 4.00,No,No,Yes,No,No
+8/7/2020 15:14,Female,18,Engine,year 4,3.50 - 4.00,No,No,No,No,No
+8/7/2020 15:14,Male,24,BCS,year 2,3.00 - 3.49,No,Yes,No,No,No
+8/7/2020 15:18,Female,24,BCS,year 3,3.50 - 4.00,No,No,No,Yes,No
+8/7/2020 15:27,Female,23,ALA,year 1,2.50 - 2.99,Yes,Yes,No,Yes,Yes
+8/7/2020 15:37,Female,18,BCS,year 2,3.50 - 4.00,No,No,Yes,No,No
+8/7/2020 15:47,Female,19,Biomedical science,year 3,3.00 - 3.49,No,No,No,No,No
+8/7/2020 15:48,Female,20,koe,year 3,3.00 - 3.49,Yes,Yes,Yes,Yes,No
+8/7/2020 15:57,Female,19,BCS,year 1,3.50 - 4.00,No,Yes,No,Yes,Yes
+8/7/2020 15:58,Male,21,BCS,year 1,3.00 - 3.49,No,No,No,No,No
+8/7/2020 16:08,Male,23,Kirkhs,Year 3,3.50 - 4.00,No,No,No,No,No
+8/7/2020 16:21,Female,20,BENL,Year 3,3.00 - 3.49,No,Yes,Yes,No,No
+8/7/2020 16:22,Female,18,BCS,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 16:34,Female,23,Benl,year 1,3.00 - 3.49,No,No,No,No,No
+8/7/2020 16:34,Female,18,IT,Year 3,3.00 - 3.49,No,No,No,Yes,No
+8/7/2020 16:53,Female,19,BCS,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 17:05,Female,18,CTS,Year 1,3.50 - 4.00,No,No,No,Yes,No
+8/7/2020 17:37,Female,24,engin,year 1,3.50 - 4.00,No,No,No,Yes,No
+8/7/2020 17:46,Female,24,Engine,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 17:50,Female,23,Econs,year 1,3.50 - 4.00,No,Yes,Yes,No,No
+8/7/2020 18:10,Female,18,KOE,Year 3,3.00 - 3.49,No,No,Yes,No,No
+8/7/2020 18:11,Male,19,MHSC,Year 3,3.00 - 3.49,Yes,Yes,No,Yes,No
+8/7/2020 19:05,Female,18,Malcom,year 1,3.50 - 4.00,No,Yes,No,No,No
+8/7/2020 19:32,Female,24,Kop,year 4,3.00 - 3.49,No,No,Yes,No,No
+8/7/2020 20:36,Female,24,Biomedical science,year 1,3.00 - 3.49,No,No,No,No,No
+8/7/2020 21:21,Female,18,Laws,Year 3,3.50 - 4.00,No,No,No,Yes,No
+8/7/2020 22:35,Female,19,BIT,Year 3,3.00 - 3.49,Yes,Yes,No,No,No
+9/7/2020 6:57,Male,18,Biomedical science,year 1,0 - 1.99,No,No,No,No,No
+9/7/2020 11:43,Male,24,BIT,Year 3,3.50 - 4.00,No,No,Yes,No,No
+9/7/2020 11:57,Female,24,KOE,year 1,3.50 - 4.00,No,No,Yes,Yes,No
+9/7/2020 13:15,Female,23,Engineering,year 1,3.00 - 3.49,No,Yes,No,No,No
+9/7/2020 18:24,Female,18,Human Sciences ,Year 2,3.00 - 3.49,No,No,No,Yes,No
+13/07/2020 10:07:32,Female,19,Biotechnology,Year 3,0 - 1.99,No,No,No,No,No
+13/07/2020 10:10:30,Female,18,Engineering,year 4,3.50 - 4.00,No,No,No,No,No
+13/07/2020 10:11:26,Female,24,Communication ,Year 2,3.50 - 4.00,Yes,Yes,Yes,Yes,No
+13/07/2020 10:12:18,Female,24,Diploma Nursing,year 2,3.50 - 4.00,No,No,No,No,No
+13/07/2020 10:12:26,Female,19,Engineering,year 1,3.00 - 3.49,No,Yes,Yes,No,No
+13/07/2020 10:12:28,Female,19,Pendidikan Islam ,Year 2,3.00 - 3.49,No,No,No,No,No
+13/07/2020 10:14:46,Male,23,Radiography,year 1,3.00 - 3.49,No,No,No,No,No
+13/07/2020 10:33:47,Female,18,psychology,year 1,3.50 - 4.00,No,Yes,Yes,No,Yes
+13/07/2020 10:34:08,Female,19,Fiqh fatwa ,Year 3,3.00 - 3.49,No,No,No,No,No
+13/07/2020 11:46:13,Female,18,psychology,year 1,3.50 - 4.00,No,Yes,Yes,Yes,No
+13/07/2020 11:49:02,Male,24,BIT,year 1,3.00 - 3.49,No,No,Yes,No,No
+13/07/2020 11:54:58,Male,24,Engineering,Year 2,2.00 - 2.49,No,No,No,Yes,No
+13/07/2020 13:57:11,Female,23,DIPLOMA TESL,Year 3,3.50 - 4.00,No,No,No,Yes,No
+13/07/2020 14:38:12,Male,18,Koe,Year 2,3.00 - 3.49,No,No,Yes,No,No
+13/07/2020 14:48:05,Female,19,KOE,year 2,3.00 - 3.49,Yes,Yes,No,No,No
+13/07/2020 16:15:13,Female,18,BENL,year 1,3.00 - 3.49,No,Yes,No,No,No
+13/07/2020 17:30:44,Female,24,Fiqh,Year 3,0 - 1.99,No,No,No,Yes,No
+13/07/2020 19:08:32,Female,18,Islamic Education,year 1,3.50 - 4.00,No,No,No,No,No
+13/07/2020 19:56:49,Female,21,BCS,year 1,3.50 - 4.00,No,No,Yes,No,No
+13/07/2020 21:21:42,Male,18,Engineering,Year 2,3.00 - 3.49,No,Yes,Yes,No,No
+13/07/2020 21:22:56,Female,19,Nursing ,Year 3,3.50 - 4.00,Yes,Yes,No,Yes,No
+13/07/2020 21:23:57,Female,23,Pendidikan Islam,year 4,3.50 - 4.00,No,No,No,No,No
+18/07/2020 20:16:21,Male,20,Biomedical science,Year 2,3.00 - 3.49,No,No,No,No,No
diff --git a/Homework2/vkosuri/React-Template/index.html b/Homework2/vkosuri/React-Template/index.html
new file mode 100644
index 0000000..a4a4009
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ Vite + React + TS
+
+
+
+
+
+
+
diff --git a/Homework2/vkosuri/React-Template/package.json b/Homework2/vkosuri/React-Template/package.json
new file mode 100644
index 0000000..3603f6b
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "react-template",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@emotion/react": "^11.13.3",
+ "@emotion/styled": "^11.13.0",
+ "@mui/material": "^5.16.7",
+ "@types/d3": "^7.4.3",
+ "@types/lodash": "^4.17.7",
+ "@types/styled-components": "^5.1.34",
+ "axios": "^1.2.1",
+ "d3": "^7.9.0",
+ "d3-sankey": "^0.12.3",
+ "lodash": "^4.17.21",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "styled-components": "^6.1.13",
+ "usehooks-ts": "^3.1.0"
+ },
+ "devDependencies": {
+ "@types/d3-sankey": "^0.12.4",
+ "@types/material-ui": "^0.21.17",
+ "@types/react": "^18.3.3",
+ "@types/react-dom": "^18.3.0",
+ "@vitejs/plugin-react": "^4.3.1",
+ "globals": "^15.9.0",
+ "typescript": "^5.5.3",
+ "vite": "^5.4.1"
+ }
+}
diff --git a/Homework2/vkosuri/React-Template/src/App.tsx b/Homework2/vkosuri/React-Template/src/App.tsx
new file mode 100644
index 0000000..8fe7d9b
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/src/App.tsx
@@ -0,0 +1,50 @@
+import React from 'react';
+import BarChart from './components/BarChart';
+import PieChart from './components/PieChart';
+import SankeyChart from './components/SankeyChart';
+import './style.css';
+
+function App() {
+ return (
+
+ {/* Header - 5% */}
+
+
Student Mental Health Dashboard - Analysis of mental health conditions among students
+
+
+ {/* Main Content - 95% */}
+
+ {/* Sankey Diagram - 61% */}
+
+
+
Student Mental Health Overview: Sankey Diagram
+
+
+
+
+
+
+ {/* Bottom Section - 34% total (18% each, Bar Chart height reduced by half) */}
+
+ {/* Bar Chart - Height reduced by 1/2 */}
+
+
Mental Health by Academic Performance: Bar Chart
+
+
+
+
+
+ {/* Pie Chart - Normal height */}
+
+
Mental Health by Gender: Pie Chart
+
+
+
+
+
+ );
+}
+
+export default App;
\ No newline at end of file
diff --git a/Homework2/vkosuri/React-Template/src/components/BarChart.tsx b/Homework2/vkosuri/React-Template/src/components/BarChart.tsx
new file mode 100644
index 0000000..1b73b0b
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/src/components/BarChart.tsx
@@ -0,0 +1,224 @@
+import React, { useEffect, useRef } from 'react';
+import * as d3 from 'd3';
+import Grid from '@mui/material/Grid';
+
+interface MentalHealthData {
+ CGPA: string;
+ depressionCount: number;
+ anxietyCount: number;
+ panicAttackCount: number;
+ noIssuesCount: number;
+}
+
+const CHART_MARGINS = { top: 45, right: 170, bottom: 60, left: 102 };
+const CGPA_ORDER = ["0 - 1.99", "2.00 - 2.49", "2.50 - 2.99", "3.00 - 3.49", "3.50 - 4.00"];
+const COLORS = ['#87CEFA', '#9370DB', '#FF6347', '#32CD32'];
+const CHART_WIDTH = 1190;
+const CHART_HEIGHT = 388;
+
+const BarChart: React.FC = () => {
+ const svgRef = useRef(null);
+
+ useEffect(() => {
+ const fetchAndRenderData = async () => {
+ try {
+ // Load and process data
+ const csvData = await d3.csv('/data/Student Mental health.csv', d => ({
+ CGPA: d["What is your CGPA?"].trim(),
+ depression: d["Do you have Depression?"],
+ anxiety: d["Do you have Anxiety?"],
+ panicAttack: d["Do you have Panic attack?"]
+ }));
+
+ const cgpaGroups = d3.groups(csvData, (d: any) => d.CGPA);
+ const data: MentalHealthData[] = cgpaGroups.map(([CGPA, students]) => ({
+ CGPA,
+ depressionCount: students.filter((d: any) => d.depression === 'Yes').length,
+ anxietyCount: students.filter((d: any) => d.anxiety === 'Yes').length,
+ panicAttackCount: students.filter((d: any) => d.panicAttack === 'Yes').length,
+ noIssuesCount: students.filter((d: any) =>
+ d.depression === 'No' && d.anxiety === 'No' && d.panicAttack === 'No'
+ ).length
+ }));
+
+ // Clear any existing SVG content
+ d3.select(svgRef.current).selectAll("*").remove();
+
+ // Set up the SVG
+ const svg = d3.select(svgRef.current)
+ .attr('width', CHART_WIDTH)
+ .attr('height', CHART_HEIGHT)
+ .attr('viewBox', `0 0 ${CHART_WIDTH} ${CHART_HEIGHT}`)
+ .attr('preserveAspectRatio', 'xMidYMid meet');
+
+ const innerWidth = CHART_WIDTH - CHART_MARGINS.left - CHART_MARGINS.right;
+ const innerHeight = CHART_HEIGHT - CHART_MARGINS.top - CHART_MARGINS.bottom;
+
+ const chart = svg.append('g')
+ .attr('transform', `translate(${CHART_MARGINS.left}, ${CHART_MARGINS.top})`);
+
+ // Create scales with adjusted y-axis
+ const xScale = d3.scaleBand()
+ .domain(CGPA_ORDER)
+ .range([0, innerWidth])
+ .padding(0.2);
+
+ // Calculate the maximum stack height from the data
+ const stack = d3.stack()
+ .keys(['depressionCount', 'anxietyCount', 'panicAttackCount', 'noIssuesCount']);
+
+ const stackedData = stack(data);
+ const yMax = d3.max(stackedData[stackedData.length - 1], d => d[1]) || 0;
+
+ // Updated y-axis scale with calculated maximum plus 10% padding
+ const yScale = d3.scaleLinear()
+ .domain([0, yMax * 1.1])
+ .range([innerHeight, 0])
+ .nice();
+
+ // Add axes with matched font styles
+ chart.append('g')
+ .attr('transform', `translate(0, ${innerHeight})`)
+ .call(d3.axisBottom(xScale))
+ .style('color', 'white')
+ .selectAll('text')
+ .style('fill', 'white')
+ .style('font-size', '18px')
+ .style('font-weight', 'bold');
+
+ // Create y-axis with appropriate number of ticks
+ const yAxis = d3.axisLeft(yScale)
+ .ticks(8)
+ .tickFormat(d => d.toString());
+
+ chart.append('g')
+ .call(yAxis)
+ .style('color', 'white')
+ .selectAll('text')
+ .style('fill', 'white')
+ .style('font-size', '18px')
+ .style('font-weight', 'bold');
+
+ // Add axis labels
+ chart.append('text')
+ .attr('x', innerWidth / 2)
+ .attr('y', innerHeight + 45)
+ .attr('text-anchor', 'middle')
+ .style('fill', 'white')
+ .style('font-size', '20px')
+ .style('font-weight', 'bold')
+ .text('CGPA Ranges');
+
+ chart.append('text')
+ .attr('transform', 'rotate(-90)')
+ .attr('x', -innerHeight / 2)
+ .attr('y', -75)
+ .attr('text-anchor', 'middle')
+ .style('fill', 'white')
+ .style('font-size', '20px')
+ .style('font-weight', 'bold')
+ .text('Number of Students');
+
+ // Create layers with labels
+ const layers = chart.selectAll('g.layer')
+ .data(stackedData)
+ .enter()
+ .append('g')
+ .attr('class', 'layer')
+ .attr('fill', (d, i) => COLORS[i]);
+
+ // Add rectangles for each segment
+ layers.selectAll('rect')
+ .data(d => d)
+ .enter()
+ .append('rect')
+ .attr('x', d => xScale(d.data.CGPA)!)
+ .attr('y', d => yScale(d[1]))
+ .attr('height', d => yScale(d[0]) - yScale(d[1]))
+ .attr('width', xScale.bandwidth());
+
+ // Add text labels for counts
+ layers.selectAll('text')
+ .data(d => d)
+ .enter()
+ .append('text')
+ .attr('x', d => xScale(d.data.CGPA)! + xScale.bandwidth() / 2)
+ .attr('y', d => {
+ const height = yScale(d[0]) - yScale(d[1]);
+ return yScale(d[1]) + height / 2;
+ })
+ .attr('text-anchor', 'middle')
+ .attr('dominant-baseline', 'middle')
+ .style('fill', 'black')
+ .style('font-size', '14px')
+ .style('font-weight', 'bold')
+ .text(d => {
+ const value = d[1] - d[0];
+ return value > 0 ? value : '';
+ });
+
+ // Add title
+ svg.append('text')
+ .attr('x', CHART_WIDTH / 2)
+ .attr('y', 25)
+ .attr('text-anchor', 'middle')
+ .style('fill', 'white')
+ .style('font-size', '28px')
+ .style('font-weight', 'bold')
+ .text('Mental Health Issues Distribution Across CGPA Ranges');
+
+ // Add legend
+ const legend = svg.append('g')
+ .attr('transform', `translate(${CHART_WIDTH - 160}, ${CHART_MARGINS.top})`);
+
+ const legendItems = [
+ 'Depression',
+ 'Anxiety',
+ 'Panic Attack',
+ 'No Issues'
+ ];
+
+ legendItems.forEach((item, i) => {
+ const g = legend.append('g')
+ .attr('transform', `translate(0, ${i * 30})`);
+
+ g.append('rect')
+ .attr('width', 15)
+ .attr('height', 15)
+ .attr('fill', COLORS[i])
+ .attr('stroke', '#000000')
+ .attr('stroke-width', 1);
+
+ g.append('text')
+ .attr('x', 25)
+ .attr('y', 12)
+ .style('fill', 'white')
+ .style('font-size', '20px')
+ .style('font-weight', 'bold')
+ .text(item);
+ });
+
+ } catch (error) {
+ console.error('Error loading data:', error);
+ }
+ };
+
+ fetchAndRenderData();
+ }, []);
+
+ return (
+
+
+
+ );
+};
+
+export default BarChart;
\ No newline at end of file
diff --git a/Homework2/vkosuri/React-Template/src/components/PieChart.tsx b/Homework2/vkosuri/React-Template/src/components/PieChart.tsx
new file mode 100644
index 0000000..e79bba3
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/src/components/PieChart.tsx
@@ -0,0 +1,201 @@
+import React, { useEffect, useRef, useState } from 'react';
+import * as d3 from 'd3';
+
+interface MentalHealthCount {
+ condition: string;
+ count: number;
+ percentage: number;
+}
+
+interface GenderData {
+ male: MentalHealthCount[];
+ female: MentalHealthCount[];
+}
+
+const PieChart: React.FC = (): JSX.Element => {
+ const svgRef = useRef(null);
+ const [data, setData] = useState({ male: [], female: [] });
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ const csvData = await d3.csv('/data/Student Mental health.csv');
+
+ const processDataForGender = (gender: string) => {
+ const genderData = csvData.filter(d => d["Choose your gender"].trim() === gender);
+ const total = genderData.length;
+
+ const counts = {
+ depression: genderData.filter(d => d["Do you have Depression?"] === "Yes").length,
+ anxiety: genderData.filter(d => d["Do you have Anxiety?"] === "Yes").length,
+ panic: genderData.filter(d => d["Do you have Panic attack?"] === "Yes").length
+ };
+
+ const noIssues = genderData.filter(d =>
+ d["Do you have Depression?"] === "No" &&
+ d["Do you have Anxiety?"] === "No" &&
+ d["Do you have Panic attack?"] === "No"
+ ).length;
+
+ return [
+ { condition: "Depression", count: counts.depression, percentage: (counts.depression / total) * 100 },
+ { condition: "Anxiety", count: counts.anxiety, percentage: (counts.anxiety / total) * 100 },
+ { condition: "Panic Attack", count: counts.panic, percentage: (counts.panic / total) * 100 },
+ { condition: "No Mental Issues", count: noIssues, percentage: (noIssues / total) * 100 }
+ ].sort((a, b) => a.percentage - b.percentage); // Sort by percentage in ascending order
+ };
+
+ setData({
+ male: processDataForGender("Male"),
+ female: processDataForGender("Female")
+ });
+ } catch (error) {
+ console.error('Error loading CSV data:', error);
+ }
+ };
+
+ fetchData();
+ }, []);
+
+ useEffect(() => {
+ if (!data.male.length || !data.female.length) return;
+
+ const container = d3.select(svgRef.current).node()?.parentElement;
+ if (!container) return;
+
+ const width = container.clientWidth;
+ const height = container.clientHeight;
+ const radius = Math.min(width / 6, height / 3);
+
+ const svg = d3.select(svgRef.current);
+ svg.selectAll('*').remove();
+
+ svg.attr('width', width)
+ .attr('height', height)
+ .attr('viewBox', `0 0 ${width} ${height}`)
+ .attr('preserveAspectRatio', 'xMidYMid meet');
+
+ svg.append('text')
+ .attr('x', width / 2)
+ .attr('y', 25)
+ .attr('text-anchor', 'middle')
+ .style('fill', 'white')
+ .style('font-size', '20px')
+ .style('font-weight', 'bold')
+ .text('Mental Health Issues Distribution Across Gender');
+
+ const colorScale = d3.scaleOrdinal()
+ .domain(['Depression', 'Anxiety', 'Panic Attack', 'No Mental Issues'])
+ .range(['#87CEFA', '#9370DB', '#FF6347', '#32CD32']);
+
+ const createPieChart = (data: MentalHealthCount[], centerX: number, title: string) => {
+ const g = svg.append('g')
+ .attr('transform', `translate(${centerX}, ${height / 2})`);
+
+ const pie = d3.pie()
+ .value(d => d.percentage)
+ .sort(null); // Remove default sorting to maintain our manual sort
+
+ const arc = d3.arc>()
+ .innerRadius(0)
+ .outerRadius(radius);
+
+ g.selectAll('path')
+ .data(pie(data))
+ .enter()
+ .append('path')
+ .attr('d', arc)
+ .attr('fill', d => colorScale(d.data.condition))
+ .attr('stroke', '#000000')
+ .attr('stroke-width', 2);
+
+ g.selectAll('text.percentage')
+ .data(pie(data))
+ .enter()
+ .append('text')
+ .attr('class', 'percentage')
+ .attr('transform', d => {
+ const [x, y] = arc.centroid(d);
+ return `translate(${x}, ${y})`;
+ })
+ .attr('text-anchor', 'middle')
+ .attr('dy', '0.35em')
+ .style('fill', '#000000')
+ .style('font-size', '12px')
+ .style('font-weight', 'bold')
+ .text(d => `${Math.round(d.data.percentage)}%`);
+
+ g.append('text')
+ .attr('class', 'title')
+ .attr('y', radius + 30)
+ .attr('text-anchor', 'middle')
+ .style('fill', 'white')
+ .style('font-size', '16px')
+ .style('font-weight', 'bold')
+ .text(title);
+
+ const total = data.reduce((sum, d) => sum + d.count, 0);
+ g.append('text')
+ .attr('class', 'total')
+ .attr('y', radius + 55)
+ .attr('text-anchor', 'middle')
+ .style('fill', 'white')
+ .style('font-size', '14px')
+ .text(`Total: ${total} students`);
+ };
+
+ createPieChart(data.female, width * 0.3, 'Female');
+ createPieChart(data.male, width * 0.7, 'Male');
+
+ // Get conditions in order of appearance for legend (using female data as reference)
+ const legendData = data.female.map(d => d.condition);
+
+ const legend = svg.append('g')
+ .attr('transform', `translate(${width * 0.1 - 132 + 75.6}, ${height/2 - 100 + 56.7})`);
+
+ const legendSpacing = 30;
+
+ legendData.forEach((item, i) => {
+ const legendItem = legend.append('g')
+ .attr('transform', `translate(0, ${i * legendSpacing})`);
+
+ legendItem.append('rect')
+ .attr('width', 15)
+ .attr('height', 15)
+ .attr('fill', colorScale(item))
+ .attr('stroke', '#000000')
+ .attr('stroke-width', 1);
+
+ legendItem.append('text')
+ .attr('x', 25)
+ .attr('y', 12)
+ .style('fill', 'white')
+ .style('font-size', '14px')
+ .style('font-weight', 'bold')
+ .text(item);
+ });
+
+ }, [data]);
+
+ return (
+
+
+
+ );
+};
+
+export default PieChart;
\ No newline at end of file
diff --git a/Homework2/vkosuri/React-Template/src/components/SankeyChart.tsx b/Homework2/vkosuri/React-Template/src/components/SankeyChart.tsx
new file mode 100644
index 0000000..977681f
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/src/components/SankeyChart.tsx
@@ -0,0 +1,328 @@
+import React, { useEffect, useRef } from 'react';
+import * as d3 from 'd3';
+import {
+ sankey,
+ sankeyLinkHorizontal,
+ SankeyNode,
+ SankeyLink
+} from 'd3-sankey';
+
+interface NodeData {
+ name: string;
+ category: string;
+ index?: number;
+ x0?: number;
+ x1?: number;
+ y0?: number;
+ y1?: number;
+ value?: number;
+}
+
+interface LinkData {
+ source: number | NodeData;
+ target: number | NodeData;
+ value: number;
+ width?: number;
+}
+
+type SankeyNodeExtended = Required>;
+type SankeyLinkExtended = Required>;
+
+const SankeyDiagram: React.FC = () => {
+ const svgRef = useRef(null);
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ const data = await d3.csv('/data/Student Mental health.csv');
+
+ const width = 5760;
+ const height = 1344;
+ const margin = {
+ top: 225.39,
+ right: 640,
+ bottom: 240,
+ left: 640
+ };
+
+ // Process data for Sankey diagram
+ const nodes: NodeData[] = [
+ { name: "18 or younger", category: "age" },
+ { name: "19", category: "age" },
+ { name: "20", category: "age" },
+ { name: "21", category: "age" },
+ { name: "22", category: "age" },
+ { name: "23", category: "age" },
+ { name: "24+", category: "age" },
+ { name: "Depression", category: "condition" },
+ { name: "Anxiety", category: "condition" },
+ { name: "Panic Attack", category: "condition" },
+ { name: "No Mental Issues", category: "condition" },
+ { name: "Sought Treatment", category: "treatment" },
+ { name: "No Treatment", category: "treatment" }
+ ];
+
+ const links: LinkData[] = [];
+
+ // Process each student's data
+ data.forEach(d => {
+ const ageGroup = getAgeGroup(d["Age"]);
+ if (!ageGroup) return;
+
+ const ageIndex = nodes.findIndex(n => n.name === ageGroup);
+ const hasCondition = d["Do you have Depression?"] === "Yes" ||
+ d["Do you have Anxiety?"] === "Yes" ||
+ d["Do you have Panic attack?"] === "Yes";
+
+ // Age to Conditions links
+ if (d["Do you have Depression?"] === "Yes") {
+ addOrUpdateLink(links, ageIndex, 7);
+ }
+ if (d["Do you have Anxiety?"] === "Yes") {
+ addOrUpdateLink(links, ageIndex, 8);
+ }
+ if (d["Do you have Panic attack?"] === "Yes") {
+ addOrUpdateLink(links, ageIndex, 9);
+ }
+ if (!hasCondition) {
+ addOrUpdateLink(links, ageIndex, 10);
+ }
+
+ // Conditions to Treatment links
+ const soughtTreatment = d["Did you seek any specialist for a treatment?"] === "Yes";
+ if (hasCondition) {
+ if (d["Do you have Depression?"] === "Yes") {
+ addOrUpdateLink(links, 7, soughtTreatment ? 11 : 12);
+ }
+ if (d["Do you have Anxiety?"] === "Yes") {
+ addOrUpdateLink(links, 8, soughtTreatment ? 11 : 12);
+ }
+ if (d["Do you have Panic attack?"] === "Yes") {
+ addOrUpdateLink(links, 9, soughtTreatment ? 11 : 12);
+ }
+ }
+ if (!hasCondition) {
+ addOrUpdateLink(links, 10, 12);
+ }
+ });
+
+ const svg = d3.select(svgRef.current);
+ svg.selectAll("*").remove();
+
+ svg.attr("width", width)
+ .attr("height", height)
+ .attr("viewBox", `0 0 ${width} ${height}`)
+ .attr("preserveAspectRatio", "xMidYMid meet");
+
+ const sankeyGenerator = sankey()
+ .nodeWidth(400)
+ .nodePadding(45)
+ .extent([[margin.left, margin.top], [width - margin.right, height - margin.bottom]]);
+
+ sankeyGenerator.nodeSort((a, b) => {
+ if (a.category === 'age' && b.category === 'age') {
+ const getAgeValue = (name: string) => {
+ if (name === "18 or younger") return 18;
+ if (name === "24+") return 24;
+ return parseInt(name);
+ };
+ return getAgeValue(a.name) - getAgeValue(b.name);
+ }
+ return 0;
+ });
+
+ const { nodes: sankeyNodes, links: sankeyLinks } = sankeyGenerator({
+ nodes: nodes.map((d, i) => ({ ...d, index: i })),
+ links: links
+ });
+
+ // Color scales
+ const ageColorScale = d3.scaleOrdinal()
+ .domain(["18 or younger", "19", "20", "21", "22", "23", "24+"])
+ .range(['#9c27b0', '#7e57c2', '#5c6bc0', '#42a5f5', '#26c6da', '#26a69a', '#2e7d32']);
+
+ const conditionColors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4'];
+ const treatmentColors = ['#00C853', '#FF5252'];
+
+ const getNodeColor = (node: SankeyNodeExtended) => {
+ if (node.category === 'age') {
+ return ageColorScale(node.name);
+ } else if (node.category === 'condition') {
+ return conditionColors[node.index - 7];
+ } else {
+ return treatmentColors[node.index - 11];
+ }
+ };
+
+ // Add links
+ const linkGroup = svg.append("g")
+ .attr("class", "links")
+ .selectAll("path")
+ .data(sankeyLinks)
+ .enter()
+ .append("path")
+ .attr("d", sankeyLinkHorizontal())
+ .style("fill", "none")
+ .style("stroke", (d) => {
+ const sourceNode = d.source as SankeyNodeExtended;
+ return d3.rgb(getNodeColor(sourceNode)).darker(0.3).toString();
+ })
+ .style("stroke-width", d => Math.max(2, (d as SankeyLinkExtended).width))
+ .style("stroke-opacity", 0.4);
+
+ // Add nodes
+ const nodeGroup = svg.append("g")
+ .attr("class", "nodes")
+ .selectAll("g")
+ .data(sankeyNodes)
+ .enter()
+ .append("g");
+
+ nodeGroup.append("rect")
+ .attr("x", d => (d as SankeyNodeExtended).x0!)
+ .attr("y", d => (d as SankeyNodeExtended).y0!)
+ .attr("height", d => (d as SankeyNodeExtended).y1! - (d as SankeyNodeExtended).y0!)
+ .attr("width", d => (d as SankeyNodeExtended).x1! - (d as SankeyNodeExtended).x0!)
+ .style("fill", d => getNodeColor(d as SankeyNodeExtended))
+ .style("stroke", "#000")
+ .style("stroke-width", 2);
+
+ // Add node labels
+ nodeGroup.append("text")
+ .attr("x", d => {
+ const node = d as SankeyNodeExtended;
+ return (node.x0! + node.x1!) / 2;
+ })
+ .attr("y", d => {
+ const node = d as SankeyNodeExtended;
+ return (node.y1! + node.y0!) / 2;
+ })
+ .attr("dy", "0.35em")
+ .attr("text-anchor", "middle")
+ .attr("fill", "white")
+ .style("font-size", "40px")
+ .style("font-weight", "bold")
+ .text(d => `${(d as SankeyNodeExtended).name} (${(d as SankeyNodeExtended).value})`);
+
+ // Add category labels
+ const categories = [
+ { text: "Age Groups", x: margin.left + 400, y: margin.top - 68 },
+ { text: "Mental Health Conditions", x: width/2, y: margin.top - 68 },
+ { text: "Treatment Status", x: width - margin.right - 400, y: margin.top - 68 }
+ ];
+
+ svg.selectAll(".category-label")
+ .data(categories)
+ .enter()
+ .append("text")
+ .attr("x", d => d.x)
+ .attr("y", d => d.y)
+ .attr("text-anchor", "middle")
+ .attr("fill", "white")
+ .style("font-size", "72px")
+ .style("font-weight", "bold")
+ .text(d => d.text);
+
+ // Add title
+ svg.append("text")
+ .attr("x", width / 2)
+ .attr("y", margin.top / 2 - 113.39)
+ .attr("text-anchor", "middle")
+ .attr("fill", "white")
+ .style("font-size", "86px")
+ .style("font-weight", "bold")
+ .text("Student Mental Health Flow Analysis");
+
+ // Add legend with reduced spacing
+ const legendData = [
+ { category: "Age Groups", colors: ageColorScale.range(), labels: ageColorScale.domain() },
+ { category: "Conditions", colors: conditionColors, labels: ["Depression", "Anxiety", "Panic Attack", "No Mental Issues"] },
+ { category: "Treatment", colors: treatmentColors, labels: ["Sought Treatment", "No Treatment"] }
+ ];
+
+ const cmToPixels = 37.795275591;
+ const legendYOffset = 3 * cmToPixels;
+
+ const legendGroup = svg.append("g")
+ .attr("class", "legend")
+ .attr("transform", `translate(${margin.left - 113.39 - 188.98 - 188.98}, ${height - margin.bottom + 60 + legendYOffset})`);
+
+ let xOffset = 0;
+ legendData.forEach((categoryData, categoryIndex) => {
+ // Add category label with slant
+ legendGroup.append("text")
+ .attr("x", xOffset)
+ .attr("y", -20)
+ .attr("transform", `rotate(-45, ${xOffset}, -20)`)
+ .attr("fill", "white")
+ .style("font-size", "72px")
+ .style("font-weight", "bold")
+ .text(categoryData.category);
+
+ const itemGroup = legendGroup.append("g")
+ .attr("transform", `translate(${xOffset}, 20)`);
+
+ // Reduced spacing for labels
+ const labelSpacing = categoryData.category === "Treatment" ? 200 : 300; // Reduced from 300/500
+
+ categoryData.colors.forEach((color, i) => {
+ const g = itemGroup.append("g")
+ .attr("transform", `translate(${i * labelSpacing}, 0)`);
+
+ g.append("rect")
+ .attr("width", 40)
+ .attr("height", 40)
+ .attr("fill", color)
+ .attr("stroke", "white")
+ .attr("stroke-width", 2);
+
+ g.append("text")
+ .attr("x", 60)
+ .attr("y", 20)
+ .attr("transform", "rotate(-45, 60, 20)")
+ .attr("fill", "white")
+ .style("font-size", "64px")
+ .text(categoryData.labels[i]);
+ });
+
+ // Reduced spacing between categories
+ xOffset += (categoryData.colors.length * labelSpacing) + 200; // Reduced from 400
+ });
+
+ } catch (error) {
+ console.error('Error loading CSV data:', error);
+ }
+ };
+
+ fetchData();
+ }, []);
+
+ return (
+
+
+
+ );
+};
+
+const getAgeGroup = (age: string) => {
+ const ageNum = parseInt(age);
+ if (isNaN(ageNum)) return null;
+ if (ageNum <= 18) return "18 or younger";
+ if (ageNum === 19) return "19";
+ if (ageNum === 20) return "20";
+ if (ageNum === 21) return "21";
+ if (ageNum === 22) return "22";
+ if (ageNum === 23) return "23";
+ return "24+";
+};
+
+const addOrUpdateLink = (links: LinkData[], source: number, target: number) => {
+ const existingLink = links.find(l =>
+ (typeof l.source === 'number' ? l.source : l.source.index) === source &&
+ (typeof l.target === 'number' ? l.target : l.target.index) === target
+ );
+ if (existingLink) existingLink.value++;
+ else links.push({ source, target, value: 1 });
+};
+
+export default SankeyDiagram;
\ No newline at end of file
diff --git a/Homework2/vkosuri/React-Template/src/main.tsx b/Homework2/vkosuri/React-Template/src/main.tsx
new file mode 100644
index 0000000..87e2790
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import App from './App.tsx'
+import './style.css'
+
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+)
diff --git a/Homework2/vkosuri/React-Template/src/stores/Reducer.ts b/Homework2/vkosuri/React-Template/src/stores/Reducer.ts
new file mode 100644
index 0000000..0160617
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/src/stores/Reducer.ts
@@ -0,0 +1,25 @@
+// Define action types
+export const ACTIONS = { INCREMENT: 'increment' };
+
+interface Action{
+ type: string;
+}
+
+interface State{
+ count: number;
+}
+
+// Define initial state
+const initialState: State = { count: 0 };
+
+// reducer function
+export const reducer = (state: State, action: Action) => {
+ switch (action.type) {
+ case ACTIONS.INCREMENT:
+ return { count: state.count + 1 };
+ default:
+ return state;
+ }
+}
+
+export type { State, Action };
\ No newline at end of file
diff --git a/Homework2/vkosuri/React-Template/src/style.css b/Homework2/vkosuri/React-Template/src/style.css
new file mode 100644
index 0000000..cf4f9f0
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/src/style.css
@@ -0,0 +1,202 @@
+/* Reset and base styles */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body, html {
+ background-color: #000;
+ color: #fff;
+ overflow: hidden;
+ font-family: 'Roboto', sans-serif;
+ height: 100%;
+}
+
+/* Dashboard container */
+.dashboard {
+ width: 100vw;
+ height: 100vh;
+ background-color: #000;
+ display: flex;
+ flex-direction: column;
+ padding: 5px;
+}
+
+/* Header - exactly 5% */
+.dashboard-header {
+ height: 5%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 5px;
+}
+
+.dashboard-header h1 {
+ font-size: 1.3rem;
+ color: #fff;
+ font-weight: normal;
+ white-space: nowrap;
+ text-align: center;
+ letter-spacing: 0.5px;
+ opacity: 0.95;
+}
+
+/* Main layout */
+.dashboard-layout {
+ height: 95%;
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+}
+
+/* Top section - Sankey (61%) */
+.top-section {
+ height: 61%;
+ min-height: 61%;
+}
+
+.top-section .chart-container {
+ width: 100%;
+ height: 100%;
+ padding: 10px 20px; /* More horizontal padding for Sankey */
+}
+
+/* Bottom section - Bar and Pie (18% each) */
+.bottom-section {
+ height: 34%; /* 18% + 18% = 34% total for bottom section */
+ display: flex;
+ gap: 5px;
+}
+
+/* Chart containers */
+.chart-container {
+ background-color: #000;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 4px;
+ padding: 10px;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+/* Half width for bottom charts */
+.half-width {
+ width: 50%;
+}
+
+/* Chart titles */
+.chart-container h2 {
+ font-size: 0.9rem;
+ color: #fff;
+ margin-bottom: 8px;
+ font-weight: normal;
+ white-space: nowrap;
+ opacity: 0.9;
+}
+
+/* Chart wrappers */
+.chart-wrapper {
+ flex: 1;
+ min-height: 0;
+ position: relative;
+}
+
+/* Specific wrapper styles */
+.sankey-wrapper {
+ width: 100%;
+ height: 100%;
+}
+
+.bar-wrapper {
+ height: 50% !important; /* Force half height */
+ margin: auto 0; /* Center vertically */
+}
+
+.pie-wrapper {
+ height: 100%;
+}
+
+/* SVG styling */
+svg {
+ width: 100%;
+ height: 100%;
+ background-color: #000;
+}
+
+/* Chart axis and text colors */
+.axis text {
+ fill: #fff;
+}
+
+.axis line,
+.axis path {
+ stroke: rgba(255, 255, 255, 0.2);
+}
+
+/* Legend styling */
+.legend text {
+ fill: #fff;
+ font-size: 0.8rem;
+}
+
+/* Remove scrollbars */
+::-webkit-scrollbar {
+ display: none;
+}
+
+/* Ensure consistent black background */
+.chart-container,
+.dashboard,
+body,
+html {
+ background-color: #000;
+}
+
+/* Bottom section specific styles */
+.bottom-section .chart-container {
+ padding: 10px;
+}
+
+/* Chart specific text styles */
+.chart-title {
+ fill: #fff;
+ font-size: 0.9rem;
+}
+
+.axis-label {
+ fill: #fff;
+ font-size: 0.8rem;
+}
+
+/* Sankey specific styles */
+.sankey-node text {
+ fill: #fff;
+ font-size: 0.8rem;
+}
+
+.sankey-link {
+ fill: none;
+ stroke-opacity: 0.3;
+}
+
+/* Responsive font sizes */
+@media (max-width: 1280px) {
+ .dashboard-header h1 {
+ font-size: 1.1rem;
+ }
+
+ .chart-container h2 {
+ font-size: 0.8rem;
+ }
+}
+
+@media (min-width: 1920px) {
+ .dashboard-header h1 {
+ font-size: 1.5rem;
+ }
+
+ .chart-container h2 {
+ font-size: 1rem;
+ }
+}
\ No newline at end of file
diff --git a/Homework2/vkosuri/React-Template/src/types.ts b/Homework2/vkosuri/React-Template/src/types.ts
new file mode 100644
index 0000000..ba503fc
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/src/types.ts
@@ -0,0 +1,21 @@
+// Global types and interfaces are stored here.
+export interface Margin {
+ readonly left: number;
+ readonly right: number;
+ readonly top: number;
+ readonly bottom: number;
+}
+
+export interface ComponentSize {
+ width: number;
+ height: number;
+}
+
+export interface Point {
+ readonly posX: number;
+ readonly posY: number;
+}
+
+export interface Bar{
+ readonly value: number;
+}
\ No newline at end of file
diff --git a/Homework2/vkosuri/React-Template/src/vite-env.d.ts b/Homework2/vkosuri/React-Template/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/Homework2/vkosuri/React-Template/tsconfig.json b/Homework2/vkosuri/React-Template/tsconfig.json
new file mode 100644
index 0000000..3b32131
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2023", "DOM", "DOM.Iterable", "es6"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "noImplicitAny": true,
+ "noImplicitThis": true,
+ "strictNullChecks": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+ },
+ "include": ["src", "vite.config.ts"]
+}
diff --git a/Homework2/vkosuri/React-Template/vite.config.ts b/Homework2/vkosuri/React-Template/vite.config.ts
new file mode 100644
index 0000000..198ec17
--- /dev/null
+++ b/Homework2/vkosuri/React-Template/vite.config.ts
@@ -0,0 +1,10 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ server: {
+ port: 3000,
+ },
+ plugins: [react()],
+})
diff --git a/Homework3/Interactive dashboard.png b/Homework3/Interactive dashboard.png
new file mode 100644
index 0000000..d6f44db
Binary files /dev/null and b/Homework3/Interactive dashboard.png differ
diff --git a/Homework3/vkosuri/React-Template/README.md b/Homework3/vkosuri/React-Template/README.md
new file mode 100644
index 0000000..765052f
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/README.md
@@ -0,0 +1,26 @@
+# More about the Framework
+
+
+This is a template in React and TypeScript. React has a steeper learning curve than Vue.js because it requires understanding JSX syntax and concepts like hooks and component lifecycle.
+
+If you want to use React but not with TypeScript, just remove any type specifications from the `Example.tsx`, `Notes.tsx`, and `NotesWithReducer.tsx`. You can always refer to `VanillaJS-Template/example.js` for this migration.
+
+
+## Files You Have to Care about
+
+`package.json` is where we manage the libraries we installed. Besides this, most of the files you can ignore, but **the files under `./src/` are your concern**.
+
+* `./src/main.tsx` is the root script file for React that instatinates our single page application.
+* `./src/App.tsx` is the root file for all **development** needs and is also where we manage the layout and load in components.
+* `./src/types.ts` is usually where we declare our customized types if you're planning to use it.
+* `./src/stores/` is where we manage the stores if you're planning to use it. The store is responsible for global state management.
+* `./src/components/` is where we create the components. You may have multiple components depends on your design.
+ * `Example.tsx` shows how to read `.csv` and `.json`, how component size is being watched, how a bar chart is created, and how the component updates if there are any changes.
+ * `Notes.tsx` shows the difference of **state** and **prop**, how to use MUI, and how a local state updates based on interaction.
+ * `NotesWithReducer.tsx` is equivalent to `Notes.tsx`, excepts it uses store called reducer.
+
+## Libraries Installed in this Framework
+ * D3.js v7 for visualization
+ * [axios](https://axios-http.com/docs/intro) for API.
+ * [Material UI](https://mui.com/material-ui/getting-started/) for UI components.
+ * [lodash](https://lodash.com/) for utility functions in JavaScript.
diff --git a/Homework3/vkosuri/React-Template/data/Student Mental health.csv b/Homework3/vkosuri/React-Template/data/Student Mental health.csv
new file mode 100644
index 0000000..0d75ad4
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/data/Student Mental health.csv
@@ -0,0 +1,102 @@
+Timestamp,Choose your gender,Age,What is your course?,Your current year of Study,What is your CGPA?,Marital status,Do you have Depression?,Do you have Anxiety?,Do you have Panic attack?,Did you seek any specialist for a treatment?
+8/7/2020 12:02,Female,18,Engineering,year 1,3.00 - 3.49,No,Yes,No,Yes,No
+8/7/2020 12:04,Male,21,Islamic education,year 2,3.00 - 3.49,No,No,Yes,No,No
+8/7/2020 12:05,Male,19,BIT,Year 1,3.00 - 3.49,No,Yes,Yes,Yes,No
+8/7/2020 12:06,Female,22,Laws,year 3,3.00 - 3.49,Yes,Yes,No,No,No
+8/7/2020 12:13,Male,23,Mathemathics,year 4,3.00 - 3.49,No,No,No,No,No
+8/7/2020 12:31,Male,19,Engineering,Year 2,3.50 - 4.00,No,No,No,Yes,No
+8/7/2020 12:32,Female,23,Pendidikan islam,year 2,3.50 - 4.00 ,Yes,Yes,No,Yes,No
+8/7/2020 12:33,Female,18,BCS,year 1,3.50 - 4.00,No,No,Yes,No,No
+8/7/2020 12:35,Female,19,Human Resources,Year 2,2.50 - 2.99,No,No,No,No,No
+8/7/2020 12:39,Male,18,Irkhs,year 1,3.50 - 4.00,No,No,Yes,Yes,No
+8/7/2020 12:39,Female,20,Psychology,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 12:39,Female,24,Engineering,Year 3,3.50 - 4.00,Yes,Yes,No,No,No
+8/7/2020 12:40,Female,18,BCS,year 1,3.00 - 3.49,No,Yes,No,No,No
+8/7/2020 12:41,Male,19,Engineering,year 1,3.00 - 3.49,No,No,No,No,No
+8/7/2020 12:43,Female,18,KENMS,Year 2,3.50 - 4.00,No,No,Yes,No,No
+8/7/2020 12:43,Male,24,BCS,Year 3,3.50 - 4.00,No,No,No,No,No
+8/7/2020 12:46,Female,24,Accounting ,year 3,3.00 - 3.49,No,No,No,No,No
+8/7/2020 12:52,Female,24,ENM,year 4,3.00 - 3.49,Yes,Yes,Yes,Yes,No
+8/7/2020 13:05,Female,20,BIT,Year 2,3.50 - 4.00,No,No,Yes,No,No
+8/7/2020 13:07,Female,18,Marine science,year 2,3.50 - 4.00,Yes,Yes,Yes,Yes,No
+8/7/2020 13:12,Female,19,Engineering,year 1,3.00 - 3.49,No,No,No,Yes,No
+8/7/2020 13:13,Female,18,KOE,Year 2,3.00 - 3.49,No,No,No,No,No
+8/7/2020 13:13,Female,24,BCS,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 13:15,Female,24,Engineering,year 1,3.00 - 3.49,No,No,No,No,No
+8/7/2020 13:17,Female,23,BCS,Year 3,3.50 - 4.00,No,Yes,Yes,Yes,No
+8/7/2020 13:29,Female,18,Banking Studies,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 13:35,Female,19,Engineering,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 13:41,Male,18,Engineering,Year 2,3.00 - 3.49,Yes,Yes,Yes,No,No
+8/7/2020 13:58,Female,24,BIT,Year 3,3.50 - 4.00,Yes,Yes,Yes,Yes,Yes
+8/7/2020 14:05,Female,24,BCS,year 4,3.50 - 4.00,No,No,No,No,No
+8/7/2020 14:27,Female,23,Business Administration,Year 2,3.00 - 3.49,No,No,No,No,No
+8/7/2020 14:29,Male,18,BCS,year 2,3.00 - 3.49,No,No,No,No,No
+8/7/2020 14:29,Male,19,BCS,year 1,3.50 - 4.00,No,No,No,Yes,No
+8/7/2020 14:31,Male,18,BCS,Year 2,3.50 - 4.00,Yes,Yes,Yes,No,Yes
+8/7/2020 14:41,Female,19,BIT,year 1,3.00 - 3.49,No,Yes,Yes,Yes,No
+8/7/2020 14:43,Female,18,Engineering,year 1,2.00 - 2.49,No,No,No,No,No
+8/7/2020 14:43,Female,18,Law,Year 3,3.00 - 3.49,No,Yes,Yes,No,No
+8/7/2020 14:45,Female,19,BIT,year 1,2.50 - 2.99,No,Yes,Yes,Yes,No
+8/7/2020 14:47,Female,18,KIRKHS,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 14:56,Female,24,Engineering,Year 2,2.50 - 2.99,Yes,Yes,No,Yes,Yes
+8/7/2020 14:57,Female,24,BIT,Year 3,3.00 - 3.49,No,No,Yes,No,No
+8/7/2020 14:57,Female,22,Engineering,year 4,3.50 - 4.00,No,No,No,No,No
+8/7/2020 14:58,Female,20,Usuluddin ,year 2,3.00 - 3.49,No,Yes,No,No,No
+8/7/2020 15:07,Male,,BIT,year 1,0 - 1.99,No,No,No,No,No
+8/7/2020 15:08,Male,23,TAASL,year 2,3.50 - 4.00,No,No,No,Yes,No
+8/7/2020 15:09,Male,18,BCS,year 1,3.50 - 4.00,No,No,Yes,Yes,No
+8/7/2020 15:12,Female,19,Engineering,year 1,3.50 - 4.00,No,No,Yes,No,No
+8/7/2020 15:14,Female,18,Engine,year 4,3.50 - 4.00,No,No,No,No,No
+8/7/2020 15:14,Male,24,BCS,year 2,3.00 - 3.49,No,Yes,No,No,No
+8/7/2020 15:18,Female,24,BCS,year 3,3.50 - 4.00,No,No,No,Yes,No
+8/7/2020 15:27,Female,23,ALA,year 1,2.50 - 2.99,Yes,Yes,No,Yes,Yes
+8/7/2020 15:37,Female,18,BCS,year 2,3.50 - 4.00,No,No,Yes,No,No
+8/7/2020 15:47,Female,19,Biomedical science,year 3,3.00 - 3.49,No,No,No,No,No
+8/7/2020 15:48,Female,20,koe,year 3,3.00 - 3.49,Yes,Yes,Yes,Yes,No
+8/7/2020 15:57,Female,19,BCS,year 1,3.50 - 4.00,No,Yes,No,Yes,Yes
+8/7/2020 15:58,Male,21,BCS,year 1,3.00 - 3.49,No,No,No,No,No
+8/7/2020 16:08,Male,23,Kirkhs,Year 3,3.50 - 4.00,No,No,No,No,No
+8/7/2020 16:21,Female,20,BENL,Year 3,3.00 - 3.49,No,Yes,Yes,No,No
+8/7/2020 16:22,Female,18,BCS,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 16:34,Female,23,Benl,year 1,3.00 - 3.49,No,No,No,No,No
+8/7/2020 16:34,Female,18,IT,Year 3,3.00 - 3.49,No,No,No,Yes,No
+8/7/2020 16:53,Female,19,BCS,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 17:05,Female,18,CTS,Year 1,3.50 - 4.00,No,No,No,Yes,No
+8/7/2020 17:37,Female,24,engin,year 1,3.50 - 4.00,No,No,No,Yes,No
+8/7/2020 17:46,Female,24,Engine,year 1,3.50 - 4.00,No,No,No,No,No
+8/7/2020 17:50,Female,23,Econs,year 1,3.50 - 4.00,No,Yes,Yes,No,No
+8/7/2020 18:10,Female,18,KOE,Year 3,3.00 - 3.49,No,No,Yes,No,No
+8/7/2020 18:11,Male,19,MHSC,Year 3,3.00 - 3.49,Yes,Yes,No,Yes,No
+8/7/2020 19:05,Female,18,Malcom,year 1,3.50 - 4.00,No,Yes,No,No,No
+8/7/2020 19:32,Female,24,Kop,year 4,3.00 - 3.49,No,No,Yes,No,No
+8/7/2020 20:36,Female,24,Biomedical science,year 1,3.00 - 3.49,No,No,No,No,No
+8/7/2020 21:21,Female,18,Laws,Year 3,3.50 - 4.00,No,No,No,Yes,No
+8/7/2020 22:35,Female,19,BIT,Year 3,3.00 - 3.49,Yes,Yes,No,No,No
+9/7/2020 6:57,Male,18,Biomedical science,year 1,0 - 1.99,No,No,No,No,No
+9/7/2020 11:43,Male,24,BIT,Year 3,3.50 - 4.00,No,No,Yes,No,No
+9/7/2020 11:57,Female,24,KOE,year 1,3.50 - 4.00,No,No,Yes,Yes,No
+9/7/2020 13:15,Female,23,Engineering,year 1,3.00 - 3.49,No,Yes,No,No,No
+9/7/2020 18:24,Female,18,Human Sciences ,Year 2,3.00 - 3.49,No,No,No,Yes,No
+13/07/2020 10:07:32,Female,19,Biotechnology,Year 3,0 - 1.99,No,No,No,No,No
+13/07/2020 10:10:30,Female,18,Engineering,year 4,3.50 - 4.00,No,No,No,No,No
+13/07/2020 10:11:26,Female,24,Communication ,Year 2,3.50 - 4.00,Yes,Yes,Yes,Yes,No
+13/07/2020 10:12:18,Female,24,Diploma Nursing,year 2,3.50 - 4.00,No,No,No,No,No
+13/07/2020 10:12:26,Female,19,Engineering,year 1,3.00 - 3.49,No,Yes,Yes,No,No
+13/07/2020 10:12:28,Female,19,Pendidikan Islam ,Year 2,3.00 - 3.49,No,No,No,No,No
+13/07/2020 10:14:46,Male,23,Radiography,year 1,3.00 - 3.49,No,No,No,No,No
+13/07/2020 10:33:47,Female,18,psychology,year 1,3.50 - 4.00,No,Yes,Yes,No,Yes
+13/07/2020 10:34:08,Female,19,Fiqh fatwa ,Year 3,3.00 - 3.49,No,No,No,No,No
+13/07/2020 11:46:13,Female,18,psychology,year 1,3.50 - 4.00,No,Yes,Yes,Yes,No
+13/07/2020 11:49:02,Male,24,BIT,year 1,3.00 - 3.49,No,No,Yes,No,No
+13/07/2020 11:54:58,Male,24,Engineering,Year 2,2.00 - 2.49,No,No,No,Yes,No
+13/07/2020 13:57:11,Female,23,DIPLOMA TESL,Year 3,3.50 - 4.00,No,No,No,Yes,No
+13/07/2020 14:38:12,Male,18,Koe,Year 2,3.00 - 3.49,No,No,Yes,No,No
+13/07/2020 14:48:05,Female,19,KOE,year 2,3.00 - 3.49,Yes,Yes,No,No,No
+13/07/2020 16:15:13,Female,18,BENL,year 1,3.00 - 3.49,No,Yes,No,No,No
+13/07/2020 17:30:44,Female,24,Fiqh,Year 3,0 - 1.99,No,No,No,Yes,No
+13/07/2020 19:08:32,Female,18,Islamic Education,year 1,3.50 - 4.00,No,No,No,No,No
+13/07/2020 19:56:49,Female,21,BCS,year 1,3.50 - 4.00,No,No,Yes,No,No
+13/07/2020 21:21:42,Male,18,Engineering,Year 2,3.00 - 3.49,No,Yes,Yes,No,No
+13/07/2020 21:22:56,Female,19,Nursing ,Year 3,3.50 - 4.00,Yes,Yes,No,Yes,No
+13/07/2020 21:23:57,Female,23,Pendidikan Islam,year 4,3.50 - 4.00,No,No,No,No,No
+18/07/2020 20:16:21,Male,20,Biomedical science,Year 2,3.00 - 3.49,No,No,No,No,No
diff --git a/Homework3/vkosuri/React-Template/index.html b/Homework3/vkosuri/React-Template/index.html
new file mode 100644
index 0000000..a4a4009
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ Vite + React + TS
+
+
+
+
+
+
+
diff --git a/Homework3/vkosuri/React-Template/package.json b/Homework3/vkosuri/React-Template/package.json
new file mode 100644
index 0000000..3603f6b
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "react-template",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@emotion/react": "^11.13.3",
+ "@emotion/styled": "^11.13.0",
+ "@mui/material": "^5.16.7",
+ "@types/d3": "^7.4.3",
+ "@types/lodash": "^4.17.7",
+ "@types/styled-components": "^5.1.34",
+ "axios": "^1.2.1",
+ "d3": "^7.9.0",
+ "d3-sankey": "^0.12.3",
+ "lodash": "^4.17.21",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "styled-components": "^6.1.13",
+ "usehooks-ts": "^3.1.0"
+ },
+ "devDependencies": {
+ "@types/d3-sankey": "^0.12.4",
+ "@types/material-ui": "^0.21.17",
+ "@types/react": "^18.3.3",
+ "@types/react-dom": "^18.3.0",
+ "@vitejs/plugin-react": "^4.3.1",
+ "globals": "^15.9.0",
+ "typescript": "^5.5.3",
+ "vite": "^5.4.1"
+ }
+}
diff --git a/Homework3/vkosuri/React-Template/src/App.tsx b/Homework3/vkosuri/React-Template/src/App.tsx
new file mode 100644
index 0000000..95e26bc
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/src/App.tsx
@@ -0,0 +1,225 @@
+import React, { useState, useCallback } from 'react';
+import BarChart from './components/BarChart';
+import PieChart from './components/PieChart';
+import SankeyChart from './components/SankeyChart';
+import { ChartProps, FilterState, HoverState, NodeData, SankeyProps } from './types';
+import './style.css';
+
+interface AppProps {}
+
+const App: React.FC = () => {
+ const [filters, setFilters] = useState({
+ selectedAge: null,
+ selectedCondition: null,
+ selectedTreatment: null
+ });
+
+ const [hoverState, setHoverState] = useState({
+ element: null
+ });
+
+ const [isTransitioning, setIsTransitioning] = useState(false);
+ const [activeView, setActiveView] = useState<'sankey' | 'bar' | 'pie'>('sankey');
+
+ const handleSankeySelect = useCallback((node: NodeData): void => {
+ setIsTransitioning(true);
+
+ setFilters(prev => {
+ const newFilters = { ...prev };
+ if (node.category === 'age') {
+ newFilters.selectedAge = prev.selectedAge === node.name ? null : node.name;
+ setActiveView('bar');
+ } else if (node.category === 'condition') {
+ newFilters.selectedCondition = prev.selectedCondition === node.name ? null : node.name;
+ setActiveView('pie');
+ } else if (node.category === 'treatment') {
+ newFilters.selectedTreatment = prev.selectedTreatment === node.name ? null : node.name;
+ setActiveView('bar');
+ }
+ return newFilters;
+ });
+
+ setTimeout(() => setIsTransitioning(false), 500);
+ }, []);
+
+ const handleHover = useCallback((
+ element: NonNullable | null,
+ event: React.MouseEvent
+ ): void => {
+ if (element) {
+ setHoverState({
+ element: {
+ ...element,
+ coordinates: { x: event.clientX, y: event.clientY }
+ }
+ });
+ } else {
+ setHoverState({ element: null });
+ }
+ }, []);
+
+ const commonChartProps: Omit = {
+ filters,
+ onElementHover: handleHover,
+ isTransitioning
+ };
+
+ const sankeyProps: SankeyProps = {
+ ...commonChartProps,
+ onNodeSelect: handleSankeySelect
+ };
+
+ const resetFilters = useCallback((): void => {
+ setIsTransitioning(true);
+ setFilters({
+ selectedAge: null,
+ selectedCondition: null,
+ selectedTreatment: null
+ });
+ setActiveView('sankey');
+ setTimeout(() => setIsTransitioning(false), 500);
+ }, []);
+
+ const renderFilterTag = useCallback((
+ type: keyof FilterState,
+ label: string,
+ value: string | null
+ ): JSX.Element | null => {
+ if (!value) return null;
+
+ return (
+ {
+ handleHover({
+ type: type.replace('selected', '').toLowerCase() as any,
+ name: value,
+ value: 0,
+ coordinates: { x: 0, y: 0 }
+ }, {} as React.MouseEvent)
+ }}
+ onMouseLeave={() => handleHover(null, {} as React.MouseEvent)}
+ >
+ {label}:
+ {value}
+
+
+ );
+ }, [handleHover]);
+
+ return (
+
+ {/* Header - 5% */}
+
+
Student Mental Health Dashboard - Analysis of mental health conditions among students
+
+
+ {(filters.selectedAge || filters.selectedCondition || filters.selectedTreatment) && (
+ <>
+ {renderFilterTag('selectedAge', 'Age', filters.selectedAge)}
+ {renderFilterTag('selectedCondition', 'Condition', filters.selectedCondition)}
+ {renderFilterTag('selectedTreatment', 'Treatment', filters.selectedTreatment)}
+
+
+ >
+ )}
+
+ {activeView !== 'sankey' && (
+
+ )}
+
+
+
+ {/* Main Content - 95% */}
+
+ {/* Sankey or Active Chart - 61% */}
+
+
+
+ {activeView === 'sankey' ? 'Student Mental Health Overview: Sankey Diagram' :
+ activeView === 'bar' ? 'Treatment Distribution Analysis' :
+ 'Mental Health Condition Distribution'}
+
+
+ {activeView === 'sankey' &&
}
+ {activeView === 'bar' &&
}
+ {activeView === 'pie' &&
}
+
+
+
+
+ {/* Bottom Section - 34% total */}
+ {activeView === 'sankey' && (
+
+
+
Mental Health by Academic Performance: Bar Chart
+
+
+
+
+
+
Mental Health by Gender: Pie Chart
+
+
+
+ )}
+
+
+ {/* Tooltip */}
+ {hoverState.element && (
+
+
+
+ {hoverState.element.name}
+
+
+ {hoverState.element.value && (
+
+ Count:
+ {hoverState.element.value}
+
+ )}
+ {hoverState.element.percentage && (
+
+ Percentage:
+ {hoverState.element.percentage.toFixed(1)}%
+
+ )}
+
+
+
+ )}
+
+ );
+};
+
+export default App;
\ No newline at end of file
diff --git a/Homework3/vkosuri/React-Template/src/components/BarChart.tsx b/Homework3/vkosuri/React-Template/src/components/BarChart.tsx
new file mode 100644
index 0000000..407decd
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/src/components/BarChart.tsx
@@ -0,0 +1,325 @@
+import React, { useEffect, useRef } from 'react';
+import * as d3 from 'd3';
+import { ChartProps } from '../types';
+
+const CHART_MARGINS = { top: 45, right: 170, bottom: 60, left: 102 };
+const AGE_GROUPS = ["18 or younger", "19", "20", "21", "22", "23", "24+"];
+const COLORS = {
+ soughtTreatment: '#00C853',
+ noTreatment: '#FF5252'
+};
+const CHART_WIDTH = 1190;
+const CHART_HEIGHT = 388;
+
+const getAgeGroup = (age: string): string | null => {
+ const ageNum = parseInt(age);
+ if (isNaN(ageNum)) return null;
+ if (ageNum <= 18) return "18 or younger";
+ if (ageNum === 19) return "19";
+ if (ageNum === 20) return "20";
+ if (ageNum === 21) return "21";
+ if (ageNum === 22) return "22";
+ if (ageNum === 23) return "23";
+ return "24+";
+};
+
+const BarChart: React.FC = ({ filters, onElementHover, isTransitioning }) => {
+ const svgRef = useRef(null);
+
+ useEffect(() => {
+ const fetchAndRenderData = async () => {
+ try {
+ const data = await d3.csv('/data/Student Mental health.csv');
+
+ // Process data matching Sankey chart logic
+ const processedData = data.reduce((acc: any, row) => {
+ const ageGroup = getAgeGroup(row.Age);
+ if (!ageGroup) return acc;
+
+ if (!acc[ageGroup]) {
+ acc[ageGroup] = {
+ ageGroup,
+ soughtTreatment: 0,
+ noTreatment: 0,
+ totalConditions: 0
+ };
+ }
+
+ const hasDepression = row["Do you have Depression?"] === "Yes";
+ const hasAnxiety = row["Do you have Anxiety?"] === "Yes";
+ const hasPanicAttack = row["Do you have Panic attack?"] === "Yes";
+ const hasCondition = hasDepression || hasAnxiety || hasPanicAttack;
+ const soughtTreatment = row["Did you seek any specialist for a treatment?"] === "Yes";
+
+ if (!hasCondition) {
+ acc[ageGroup].noTreatment++;
+ } else {
+ if (hasDepression) {
+ if (soughtTreatment) acc[ageGroup].soughtTreatment++;
+ else acc[ageGroup].noTreatment++;
+ }
+ if (hasAnxiety) {
+ if (soughtTreatment) acc[ageGroup].soughtTreatment++;
+ else acc[ageGroup].noTreatment++;
+ }
+ if (hasPanicAttack) {
+ if (soughtTreatment) acc[ageGroup].soughtTreatment++;
+ else acc[ageGroup].noTreatment++;
+ }
+ }
+
+ return acc;
+ }, {});
+
+ const chartData = AGE_GROUPS.map(age => ({
+ ageGroup: age,
+ soughtTreatment: processedData[age]?.soughtTreatment || 0,
+ noTreatment: processedData[age]?.noTreatment || 0,
+ total: (processedData[age]?.soughtTreatment || 0) + (processedData[age]?.noTreatment || 0)
+ }));
+
+ const svg = d3.select(svgRef.current);
+ svg.selectAll("*").remove();
+
+ svg.attr('width', CHART_WIDTH)
+ .attr('height', CHART_HEIGHT)
+ .attr('viewBox', `0 0 ${CHART_WIDTH} ${CHART_HEIGHT}`)
+ .attr('preserveAspectRatio', 'xMidYMid meet');
+
+ const innerWidth = CHART_WIDTH - CHART_MARGINS.left - CHART_MARGINS.right;
+ const innerHeight = CHART_HEIGHT - CHART_MARGINS.top - CHART_MARGINS.bottom;
+
+ const chart = svg.append('g')
+ .attr('transform', `translate(${CHART_MARGINS.left}, ${CHART_MARGINS.top})`);
+
+ const xScale = d3.scaleBand()
+ .domain(AGE_GROUPS)
+ .range([0, innerWidth])
+ .padding(0.2);
+
+ // Adjust yMax based on selected treatment type
+ let yMax;
+ if (filters?.selectedTreatment === 'Sought Treatment') {
+ yMax = d3.max(chartData, d => d.soughtTreatment) || 0;
+ } else if (filters?.selectedTreatment === 'No Treatment') {
+ yMax = d3.max(chartData, d => d.noTreatment) || 0;
+ } else {
+ yMax = d3.max(chartData, d => d.total) || 0;
+ }
+
+ const yScale = d3.scaleLinear()
+ .domain([0, yMax])
+ .range([innerHeight, 0])
+ .nice();
+
+ // Add the bars for each age group
+ chartData.forEach(d => {
+ const x = xScale(d.ageGroup);
+ if (x === undefined) return;
+
+ // Only show No Treatment bars if no filter or explicitly selected
+ if ((filters?.selectedTreatment === 'No Treatment' || !filters?.selectedTreatment) && d.noTreatment > 0) {
+ const height = innerHeight - yScale(d.noTreatment);
+ chart.append('rect')
+ .attr('x', x)
+ .attr('y', yScale(d.noTreatment))
+ .attr('width', xScale.bandwidth())
+ .attr('height', height)
+ .attr('fill', COLORS.noTreatment)
+ .style('cursor', 'pointer')
+ .on('mouseenter', (event) => {
+ d3.select(event.target)
+ .style('opacity', 0.8)
+ .style('stroke', 'white')
+ .style('stroke-width', 2);
+
+ onElementHover({
+ type: 'bar',
+ name: d.ageGroup,
+ category: 'No Treatment',
+ value: d.noTreatment,
+ percentage: (d.noTreatment / d.total) * 100,
+ coordinates: { x: event.pageX, y: event.pageY }
+ }, event);
+ })
+ .on('mouseleave', (event) => {
+ d3.select(event.target)
+ .style('opacity', 1)
+ .style('stroke', 'none');
+ onElementHover(null, event);
+ });
+
+ if (!filters?.selectedTreatment || filters?.selectedTreatment === 'No Treatment') {
+ chart.append('text')
+ .attr('x', x + xScale.bandwidth() / 2)
+ .attr('y', yScale(d.noTreatment) + height / 2)
+ .attr('text-anchor', 'middle')
+ .attr('dominant-baseline', 'middle')
+ .style('fill', 'white')
+ .style('font-size', '14px')
+ .style('font-weight', 'bold')
+ .text(d.noTreatment);
+ }
+ }
+
+ // Only show Sought Treatment bars if no filter or explicitly selected
+ if ((filters?.selectedTreatment === 'Sought Treatment' || !filters?.selectedTreatment) && d.soughtTreatment > 0) {
+ const baseY = filters?.selectedTreatment === 'Sought Treatment' ? innerHeight : yScale(d.noTreatment);
+ const height = filters?.selectedTreatment === 'Sought Treatment'
+ ? innerHeight - yScale(d.soughtTreatment)
+ : yScale(d.noTreatment) - yScale(d.total);
+ const barY = filters?.selectedTreatment === 'Sought Treatment'
+ ? yScale(d.soughtTreatment)
+ : yScale(d.total);
+
+ chart.append('rect')
+ .attr('x', x)
+ .attr('y', barY)
+ .attr('width', xScale.bandwidth())
+ .attr('height', height)
+ .attr('fill', COLORS.soughtTreatment)
+ .style('cursor', 'pointer')
+ .on('mouseenter', (event) => {
+ d3.select(event.target)
+ .style('opacity', 0.8)
+ .style('stroke', 'white')
+ .style('stroke-width', 2);
+
+ onElementHover({
+ type: 'bar',
+ name: d.ageGroup,
+ category: 'Sought Treatment',
+ value: d.soughtTreatment,
+ percentage: (d.soughtTreatment / d.total) * 100,
+ coordinates: { x: event.pageX, y: event.pageY }
+ }, event);
+ })
+ .on('mouseleave', (event) => {
+ d3.select(event.target)
+ .style('opacity', 1)
+ .style('stroke', 'none');
+ onElementHover(null, event);
+ });
+
+ if (!filters?.selectedTreatment || filters?.selectedTreatment === 'Sought Treatment') {
+ chart.append('text')
+ .attr('x', x + xScale.bandwidth() / 2)
+ .attr('y', barY + height / 2)
+ .attr('text-anchor', 'middle')
+ .attr('dominant-baseline', 'middle')
+ .style('fill', 'white')
+ .style('font-size', '14px')
+ .style('font-weight', 'bold')
+ .text(d.soughtTreatment);
+ }
+ }
+ });
+
+ // X-axis with straight labels
+ chart.append('g')
+ .attr('transform', `translate(0, ${innerHeight})`)
+ .call(d3.axisBottom(xScale))
+ .style('color', 'white')
+ .selectAll('text')
+ .style('fill', 'white')
+ .style('font-size', '18px')
+ .style('font-weight', 'bold')
+ .style('text-anchor', 'middle');
+
+ // Y-axis
+ chart.append('g')
+ .call(d3.axisLeft(yScale).ticks(5))
+ .style('color', 'white')
+ .selectAll('text')
+ .style('fill', 'white')
+ .style('font-size', '18px')
+ .style('font-weight', 'bold');
+
+ // Title
+ svg.append('text')
+ .attr('x', CHART_WIDTH / 2)
+ .attr('y', 25)
+ .attr('text-anchor', 'middle')
+ .style('fill', 'white')
+ .style('font-size', '28px')
+ .style('font-weight', 'bold')
+ .style('opacity', isTransitioning ? 0.5 : 1)
+ .text('Treatment Status Distribution by Age Group');
+
+ // X-axis label
+ chart.append('text')
+ .attr('x', innerWidth / 2)
+ .attr('y', innerHeight + 45)
+ .attr('text-anchor', 'middle')
+ .style('fill', 'white')
+ .style('font-size', '20px')
+ .style('font-weight', 'bold')
+ .text('Age Groups');
+
+ // Y-axis label
+ chart.append('text')
+ .attr('transform', 'rotate(-90)')
+ .attr('x', -innerHeight / 2)
+ .attr('y', -75)
+ .attr('text-anchor', 'middle')
+ .style('fill', 'white')
+ .style('font-size', '20px')
+ .style('font-weight', 'bold')
+ .text('Number of Students');
+
+ // Show legend only when no specific treatment is selected
+ if (!filters?.selectedTreatment) {
+ const legend = svg.append('g')
+ .attr('transform', `translate(${CHART_WIDTH - 160}, ${CHART_MARGINS.top})`);
+
+ const legendItems = [
+ { key: 'soughtTreatment', label: 'Sought Treatment' },
+ { key: 'noTreatment', label: 'No Treatment' }
+ ];
+
+ legendItems.forEach((item, i) => {
+ const g = legend.append('g')
+ .attr('transform', `translate(0, ${i * 30})`)
+ .style('cursor', 'pointer');
+
+ g.append('rect')
+ .attr('width', 15)
+ .attr('height', 15)
+ .attr('fill', COLORS[item.key as keyof typeof COLORS])
+ .attr('stroke', '#000000')
+ .attr('stroke-width', 1);
+
+ g.append('text')
+ .attr('x', 25)
+ .attr('y', 12)
+ .style('fill', 'white')
+ .style('font-size', '24px') // Increased from 20px to 24px
+ .style('font-weight', 'bold')
+ .text(item.label);
+ });
+ }
+
+ } catch (error) {
+ console.error('Error loading data:', error);
+ }
+ };
+
+ fetchAndRenderData();
+ }, [filters, onElementHover, isTransitioning]);
+
+ return (
+
+
+
+ );
+};
+
+export default BarChart;
\ No newline at end of file
diff --git a/Homework3/vkosuri/React-Template/src/components/PieChart.tsx b/Homework3/vkosuri/React-Template/src/components/PieChart.tsx
new file mode 100644
index 0000000..c90643f
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/src/components/PieChart.tsx
@@ -0,0 +1,282 @@
+import React, { useEffect, useRef, useMemo } from 'react';
+import * as d3 from 'd3';
+import { ChartProps, FilterState } from '../types';
+
+// Interfaces and Types
+interface MentalHealthCount {
+ condition: string;
+ count: number;
+ percentage: number;
+}
+
+type D3Selection = d3.Selection;
+type D3Transition = d3.Transition;
+type D3PieArcDatum = d3.PieArcDatum;
+
+interface D3Event extends MouseEvent {
+ currentTarget: SVGPathElement | SVGGElement;
+}
+
+// Constants
+const COLORS = {
+ Depression: '#FF69B4', // Pink
+ Anxiety: '#FFA500', // Orange
+ 'Panic Attack': '#9370DB', // Purple
+ 'No Mental Issues': '#808080' // Gray
+};
+
+const TRANSITION_DURATION = 750;
+
+// Helper Functions
+const getAgeGroup = (age: string | undefined, selectedAge: string | null): boolean => {
+ if (!age || selectedAge === null) return true;
+ const ageNum = parseInt(age);
+ if (isNaN(ageNum)) return false;
+
+ switch (selectedAge) {
+ case "18 or younger":
+ return ageNum <= 18;
+ case "24+":
+ return ageNum >= 24;
+ default:
+ return age === selectedAge;
+ }
+};
+
+// Main Component
+const PieChart: React.FC = ({ filters, onElementHover, isTransitioning }) => {
+ const svgRef = useRef(null);
+ const prevDataRef = useRef<{ male: MentalHealthCount[]; female: MentalHealthCount[]; } | null>(null);
+
+ // Data Processing Logic
+ const processData = useMemo(() => (csvData: d3.DSVRowString[]) => {
+ const processDataForGender = (gender: string) => {
+ let filteredData = csvData.filter(d => d["Choose your gender"]?.trim() === gender);
+ filteredData = filteredData.filter(d => getAgeGroup(d.Age, filters.selectedAge));
+
+ const total = filteredData.length;
+ if (total === 0) return [];
+
+ const counts = {
+ Depression: filteredData.filter(d => d["Do you have Depression?"] === "Yes").length,
+ Anxiety: filteredData.filter(d => d["Do you have Anxiety?"] === "Yes").length,
+ "Panic Attack": filteredData.filter(d => d["Do you have Panic attack?"] === "Yes").length,
+ "No Mental Issues": filteredData.filter(d =>
+ d["Do you have Depression?"] === "No" &&
+ d["Do you have Anxiety?"] === "No" &&
+ d["Do you have Panic attack?"] === "No"
+ ).length
+ };
+
+ return Object.entries(counts)
+ .map(([condition, count]) => ({
+ condition,
+ count,
+ percentage: total > 0 ? (count / total) * 100 : 0
+ }))
+ .filter(d => d.count > 0);
+ };
+
+ return {
+ male: processDataForGender("Male"),
+ female: processDataForGender("Female")
+ };
+ }, [filters]);
+
+ // Chart Rendering Logic
+ useEffect(() => {
+ const renderChart = async () => {
+ try {
+ const csvData = await d3.csv('/data/Student Mental health.csv');
+ const data = processData(csvData);
+ const prevData = prevDataRef.current;
+ prevDataRef.current = data;
+
+ const container = d3.select(svgRef.current).node()?.parentElement;
+ if (!container) return;
+
+ const width = container.clientWidth;
+ const height = container.clientHeight;
+ const radius = Math.min(width / 6, height / 3);
+
+ const svg = d3.select(svgRef.current);
+ svg.selectAll('*').remove();
+
+ svg.attr('width', width)
+ .attr('height', height)
+ .attr('viewBox', `0 0 ${width} ${height}`)
+ .attr('preserveAspectRatio', 'xMidYMid meet')
+ .style('opacity', isTransitioning ? 0 : 1)
+ .transition()
+ .duration(300)
+ .style('opacity', 1);
+
+ svg.append('text')
+ .attr('x', width / 2)
+ .attr('y', 25)
+ .attr('text-anchor', 'middle')
+ .style('fill', 'white')
+ .style('font-size', '16px')
+ .style('font-weight', 'bold')
+ .text('Mental Health Issues Distribution Across Gender');
+
+ const pie = d3.pie()
+ .value(d => d.percentage)
+ .sort((a, b) => a.percentage - b.percentage);
+
+ const arc = d3.arc()
+ .innerRadius(0)
+ .outerRadius(radius);
+
+ const createPieChart = (pieData: MentalHealthCount[], centerX: number, title: string) => {
+ const g = svg.append('g')
+ .attr('transform', `translate(${centerX}, ${height / 2})`) as unknown as D3Selection;
+
+ const segments = g.selectAll('path')
+ .data(pie(pieData))
+ .join(
+ enter => enter.append('path')
+ .attr('d', arc)
+ .style('opacity', d => filters.selectedCondition === null ? 1 :
+ d.data.condition === filters.selectedCondition ? 1 : 0.2)
+ .style('fill', d => COLORS[d.data.condition as keyof typeof COLORS])
+ .style('stroke', '#000000')
+ .style('stroke-width', 2)
+ .style('cursor', 'pointer'),
+ update => update,
+ exit => exit.remove()
+ );
+
+ segments
+ .on('mouseenter', (event: MouseEvent, d: D3PieArcDatum) => {
+ const target = event.currentTarget as SVGPathElement;
+ d3.select(target)
+ .transition()
+ .duration(200)
+ .attr('transform', 'scale(1.05)');
+
+ onElementHover({
+ type: 'pie',
+ name: d.data.condition,
+ value: d.data.count,
+ percentage: d.data.percentage,
+ coordinates: { x: event.pageX, y: event.pageY }
+ }, event as unknown as React.MouseEvent);
+ })
+ .on('mouseleave', (event: MouseEvent) => {
+ d3.select(event.currentTarget as SVGPathElement)
+ .transition()
+ .duration(200)
+ .attr('transform', 'scale(1)');
+
+ onElementHover(null, event as unknown as React.MouseEvent);
+ });
+
+ g.selectAll('text.percentage')
+ .data(pie(pieData))
+ .join('text')
+ .attr('class', 'percentage')
+ .attr('transform', d => `translate(${arc.centroid(d)})`)
+ .style('text-anchor', 'middle')
+ .style('fill', '#000000')
+ .style('font-size', '12px')
+ .style('font-weight', 'bold')
+ .style('opacity', d => filters.selectedCondition === null ? 1 :
+ d.data.condition === filters.selectedCondition ? 1 : 0.2)
+ .text(d => `${Math.round(d.data.percentage)}%`);
+
+ g.append('text')
+ .attr('class', 'title')
+ .attr('x', radius + 10)
+ .attr('y', 0)
+ .attr('text-anchor', 'start')
+ .style('fill', 'white')
+ .style('font-size', '16px')
+ .style('font-weight', 'bold')
+ .text(title);
+
+ // Add count and percentage for selected condition
+ if (filters.selectedCondition) {
+ const selectedData = pieData.find(d => d.condition === filters.selectedCondition);
+ if (selectedData) {
+ g.append('text')
+ .attr('class', 'selected-info')
+ .attr('x', radius + 10)
+ .attr('y', 30)
+ .attr('text-anchor', 'start')
+ .style('fill', 'white')
+ .style('font-size', '14px')
+ .text(`${filters.selectedCondition}: ${selectedData.count} (${selectedData.percentage.toFixed(1)}%)`);
+ }
+ }
+
+ const total = pieData.reduce((sum, d) => sum + d.count, 0);
+ g.append('text')
+ .attr('class', 'total')
+ .attr('y', radius + 30)
+ .attr('text-anchor', 'middle')
+ .style('fill', 'white')
+ .style('font-size', '14px')
+ .text(`Total: ${total} students`);
+ };
+
+ createPieChart(data.female, width * 0.3, 'Female');
+ createPieChart(data.male, width * 0.7, 'Male');
+
+ const legendData = Object.entries(COLORS);
+ const legend = svg.append('g')
+ .attr('transform', `translate(20, ${height * 0.3})`);
+
+ legendData.forEach(([condition, color], i) => {
+ const g = legend.append('g')
+ .attr('transform', `translate(0, ${i * 30})`)
+ .style('cursor', 'pointer')
+ .style('opacity', filters.selectedCondition === null ? 1 :
+ condition === filters.selectedCondition ? 1 : 0.2);
+
+ g.append('rect')
+ .attr('width', 15)
+ .attr('height', 15)
+ .attr('fill', color)
+ .attr('stroke', '#000000')
+ .attr('stroke-width', 1);
+
+ g.append('text')
+ .attr('x', 25)
+ .attr('y', 12)
+ .style('fill', 'white')
+ .style('font-size', '14px')
+ .style('font-weight', 'bold')
+ .text(condition);
+ });
+
+ } catch (error) {
+ console.error('Error loading CSV data:', error);
+ }
+ };
+
+ renderChart();
+ }, [filters, onElementHover, isTransitioning, processData]);
+
+ return (
+
+
+
+ );
+};
+
+export default PieChart;
\ No newline at end of file
diff --git a/Homework3/vkosuri/React-Template/src/components/SankeyChart.tsx b/Homework3/vkosuri/React-Template/src/components/SankeyChart.tsx
new file mode 100644
index 0000000..efa89ce
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/src/components/SankeyChart.tsx
@@ -0,0 +1,457 @@
+import React, { useEffect, useRef } from 'react';
+import * as d3 from 'd3';
+import { sankey, sankeyLinkHorizontal, SankeyNode, SankeyLink } from 'd3-sankey';
+import {
+ SankeyProps,
+ NodeData,
+ LinkData,
+ ChartDimensions,
+ Point
+} from '../types';
+
+
+interface SankeyNodeExtended extends Required> {}
+interface SankeyLinkExtended extends Required> {}
+
+
+interface D3Event extends MouseEvent {
+ currentTarget: SVGPathElement | SVGGElement;
+}
+
+
+const getAgeGroup = (age: string): string | null => {
+ const ageNum = parseInt(age);
+ if (isNaN(ageNum)) return null;
+ if (ageNum <= 18) return "18 or younger";
+ if (ageNum === 19) return "19";
+ if (ageNum === 20) return "20";
+ if (ageNum === 21) return "21";
+ if (ageNum === 22) return "22";
+ if (ageNum === 23) return "23";
+ return "24+";
+};
+
+
+const addOrUpdateLink = (links: LinkData[], source: number, target: number): void => {
+ const existingLink = links.find(l =>
+ (typeof l.source === 'number' ? l.source : l.source.index) === source &&
+ (typeof l.target === 'number' ? l.target : l.target.index) === target
+ );
+ if (existingLink) {
+ existingLink.value++;
+ } else {
+ links.push({ source, target, value: 1 });
+ }
+};
+
+
+const SankeyChart: React.FC = ({
+ onNodeSelect,
+ onElementHover,
+ filters,
+ isTransitioning
+}) => {
+ const svgRef = useRef(null);
+
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ const data = await d3.csv('/data/Student Mental health.csv');
+ const width = 5760;
+ const height = 1344;
+ const margin = {
+ top: 149.80,
+ right: 640,
+ bottom: 240,
+ left: 840 // Increased to move diagram right
+ };
+
+
+ const nodes: NodeData[] = [
+ { name: "18 or younger", category: "age" },
+ { name: "19", category: "age" },
+ { name: "20", category: "age" },
+ { name: "21", category: "age" },
+ { name: "22", category: "age" },
+ { name: "23", category: "age" },
+ { name: "24+", category: "age" },
+ { name: "Depression", category: "condition" },
+ { name: "Anxiety", category: "condition" },
+ { name: "Panic Attack", category: "condition" },
+ { name: "No Mental Issues", category: "condition" },
+ { name: "Sought Treatment", category: "treatment" },
+ { name: "No Treatment", category: "treatment" }
+ ];
+
+
+ const links: LinkData[] = [];
+
+
+ data.forEach(d => {
+ const ageGroup = getAgeGroup(d["Age"]);
+ if (!ageGroup) return;
+
+ const ageIndex = nodes.findIndex(n => n.name === ageGroup);
+ const hasCondition = d["Do you have Depression?"] === "Yes" ||
+ d["Do you have Anxiety?"] === "Yes" ||
+ d["Do you have Panic attack?"] === "Yes";
+
+
+ if (d["Do you have Depression?"] === "Yes") {
+ addOrUpdateLink(links, ageIndex, 7);
+ }
+ if (d["Do you have Anxiety?"] === "Yes") {
+ addOrUpdateLink(links, ageIndex, 8);
+ }
+ if (d["Do you have Panic attack?"] === "Yes") {
+ addOrUpdateLink(links, ageIndex, 9);
+ }
+ if (!hasCondition) {
+ addOrUpdateLink(links, ageIndex, 10);
+ }
+
+
+ const soughtTreatment = d["Did you seek any specialist for a treatment?"] === "Yes";
+ if (hasCondition) {
+ if (d["Do you have Depression?"] === "Yes") {
+ addOrUpdateLink(links, 7, soughtTreatment ? 11 : 12);
+ }
+ if (d["Do you have Anxiety?"] === "Yes") {
+ addOrUpdateLink(links, 8, soughtTreatment ? 11 : 12);
+ }
+ if (d["Do you have Panic attack?"] === "Yes") {
+ addOrUpdateLink(links, 9, soughtTreatment ? 11 : 12);
+ }
+ }
+ if (!hasCondition) {
+ addOrUpdateLink(links, 10, 12);
+ }
+ });
+
+
+ const ageColorScale = d3.scaleSequential()
+ .domain([0, 6])
+ .interpolator(d3.interpolate("#1a4c7c", "#90caf9"));
+
+
+ const conditionColors = {
+ "Depression": "#FF69B4",
+ "Anxiety": "#FFA500",
+ "Panic Attack": "#9370DB",
+ "No Mental Issues": "#808080"
+ };
+
+
+ const treatmentColors = {
+ "Sought Treatment": "#00C853",
+ "No Treatment": "#FF5252"
+ };
+
+
+ const getNodeColor = (node: SankeyNodeExtended): string => {
+ if (node.category === 'age') {
+ return ageColorScale(nodes.findIndex(n => n.name === node.name));
+ } else if (node.category === 'condition') {
+ return conditionColors[node.name as keyof typeof conditionColors];
+ } else {
+ return treatmentColors[node.name as keyof typeof treatmentColors];
+ }
+ };
+
+
+ const svg = d3.select(svgRef.current);
+ svg.selectAll("*").remove();
+
+ svg.attr("width", width)
+ .attr("height", height)
+ .attr("viewBox", `0 0 ${width} ${height}`)
+ .attr("preserveAspectRatio", "xMidYMid meet");
+
+
+ svg.append("text")
+ .attr("x", width / 2)
+ .attr("y", margin.top / 2 - 75.59)
+ .attr("text-anchor", "middle")
+ .attr("fill", "white")
+ .style("font-size", "72px")
+ .style("font-weight", "bold")
+ .text("Student Mental Health Flow Analysis");
+
+
+ const sectionTitles = ["Age Groups", "Mental Health Conditions", "Treatment Status"];
+ const sectionWidth = (width - margin.left - margin.right) / 3;
+
+ sectionTitles.forEach((title, i) => {
+ svg.append("text")
+ .attr("x", margin.left + 200 + (sectionWidth / 2) + (i * sectionWidth)) // Added 200 to move section titles right
+ .attr("y", margin.top - 50)
+ .attr("text-anchor", "middle")
+ .attr("fill", "white")
+ .style("font-size", "54px")
+ .style("font-weight", "bold")
+ .text(title);
+ });
+
+
+ const zoom = d3.zoom()
+ .scaleExtent([0.5, 2])
+ .on('zoom', (event) => {
+ svg.selectAll('g')
+ .transition()
+ .duration(200)
+ .attr('transform', event.transform);
+ });
+
+
+ svg.call(zoom);
+
+
+ const sankeyGenerator = sankey()
+ .nodeWidth(400)
+ .nodePadding(45)
+ .extent([[margin.left + 200, margin.top], [width - margin.right, height - margin.bottom]]);
+
+
+ sankeyGenerator.nodeSort((a, b) => {
+ if (a.category === 'age' && b.category === 'age') {
+ const getAgeValue = (name: string) => {
+ if (name === "18 or younger") return 18;
+ if (name === "24+") return 24;
+ return parseInt(name);
+ };
+ return getAgeValue(a.name) - getAgeValue(b.name);
+ }
+ return 0;
+ });
+
+
+ const { nodes: sankeyNodes, links: sankeyLinks } = sankeyGenerator({
+ nodes: nodes.map((d, i) => ({ ...d, index: i })),
+ links: links
+ });
+
+
+ const linkGroup = svg.append("g")
+ .attr("class", "links")
+ .selectAll("path")
+ .data(sankeyLinks)
+ .enter()
+ .append("path")
+ .attr("d", sankeyLinkHorizontal())
+ .attr("class", "sankey-link")
+ .style("fill", "none")
+ .style("stroke", (d) => {
+ const sourceNode = d.source as SankeyNodeExtended;
+ return d3.rgb(getNodeColor(sourceNode)).darker(0.3).toString();
+ })
+ .style("stroke-width", d => Math.max(2, (d as SankeyLinkExtended).width))
+ .style("stroke-opacity", 0.4)
+ .style("cursor", "pointer")
+ .on("mouseenter", (event: D3Event, d: SankeyLinkExtended) => {
+ d3.selectAll(".sankey-link")
+ .transition()
+ .duration(300)
+ .style("stroke-opacity", 0.1);
+
+
+ d3.select(event.currentTarget)
+ .transition()
+ .duration(300)
+ .style("stroke-opacity", 0.8);
+
+
+ const source = d.source as SankeyNodeExtended;
+ const target = d.target as SankeyNodeExtended;
+
+
+ onElementHover({
+ type: 'link',
+ name: `${source.name} → ${target.name}`,
+ value: d.value,
+ coordinates: { x: event.pageX, y: event.pageY }
+ }, event as unknown as React.MouseEvent);
+ })
+ .on("mouseleave", (event: D3Event) => {
+ d3.selectAll(".sankey-link")
+ .transition()
+ .duration(300)
+ .style("stroke-opacity", 0.4);
+
+
+ onElementHover(null, event as unknown as React.MouseEvent);
+ });
+
+
+ const nodeGroup = svg.append("g")
+ .attr("class", "nodes")
+ .selectAll("g")
+ .data(sankeyNodes)
+ .enter()
+ .append("g")
+ .attr("class", "node-group")
+ .style("cursor", "pointer")
+ .on("click", (event: D3Event, d: SankeyNodeExtended) => {
+ onNodeSelect(d);
+ })
+ .on("mouseenter", (event: D3Event, d: SankeyNodeExtended) => {
+ const connectedLinks = sankeyLinks.filter(link =>
+ (link.source as SankeyNodeExtended).index === d.index ||
+ (link.target as SankeyNodeExtended).index === d.index
+ );
+
+
+ d3.selectAll(".sankey-link")
+ .transition()
+ .duration(300)
+ .style("stroke-opacity", l =>
+ connectedLinks.includes(l as any) ? 0.8 : 0.1
+ );
+
+
+ onElementHover({
+ type: 'node',
+ category: d.category,
+ name: d.name,
+ value: d.value!,
+ coordinates: { x: event.pageX, y: event.pageY }
+ }, event as unknown as React.MouseEvent);
+ })
+ .on("mouseleave", (event: D3Event) => {
+ d3.selectAll(".sankey-link")
+ .transition()
+ .duration(300)
+ .style("stroke-opacity", 0.4);
+
+
+ onElementHover(null, event as unknown as React.MouseEvent);
+ });
+
+
+ nodeGroup.append("rect")
+ .attr("x", d => (d as SankeyNodeExtended).x0!)
+ .attr("y", d => (d as SankeyNodeExtended).y0!)
+ .attr("height", d => (d as SankeyNodeExtended).y1! - (d as SankeyNodeExtended).y0!)
+ .attr("width", d => (d as SankeyNodeExtended).x1! - (d as SankeyNodeExtended).x0!)
+ .style("fill", d => getNodeColor(d as SankeyNodeExtended))
+ .style("stroke", "#000")
+ .style("stroke-width", 2);
+
+
+ nodeGroup.append("text")
+ .attr("x", d => {
+ const node = d as SankeyNodeExtended;
+ return (node.x0! + node.x1!) / 2;
+ })
+ .attr("y", d => {
+ const node = d as SankeyNodeExtended;
+ return (node.y1! + node.y0!) / 2;
+ })
+ .attr("dy", "0.35em")
+ .attr("text-anchor", "middle")
+ .attr("fill", "white")
+ .style("font-size", "40px")
+ .style("font-weight", "bold")
+ .text(d => `${(d as SankeyNodeExtended).name} (${(d as SankeyNodeExtended).value})`);
+
+
+ // Updated legend positioning and styling
+ const legendGroups = [
+ {
+ title: "Age Groups",
+ items: nodes.filter(n => n.category === "age").map((n, i) => ({
+ color: ageColorScale(i),
+ label: n.name
+ }))
+ },
+ {
+ title: "Mental Health Conditions",
+ items: nodes.filter(n => n.category === "condition").map(n => ({
+ color: conditionColors[n.name as keyof typeof conditionColors],
+ label: n.name
+ }))
+ },
+ {
+ title: "Treatment Status",
+ items: nodes.filter(n => n.category === "treatment").map(n => ({
+ color: treatmentColors[n.name as keyof typeof treatmentColors],
+ label: n.name
+ }))
+ }
+ ];
+
+
+ // Adjusted starting position and spacing
+ let currentY = margin.top - 20; // Moved up more
+ const xPosition = 124.41;
+ const itemSpacing = 80; // Increased spacing between items
+ const groupSpacing = 60; // Increased spacing between groups
+
+
+ legendGroups.forEach((group, groupIndex) => {
+ const legendGroup = svg.append("g")
+ .attr("class", "legend-group")
+ .attr("transform", `translate(${xPosition}, ${currentY})`);
+
+
+ // Add group title with larger font
+ legendGroup.append("text")
+ .attr("x", 150)
+ .attr("y", 0)
+ .attr("text-anchor", "middle")
+ .attr("fill", "white")
+ .style("font-size", "56px") // Increased from 48px
+ .style("font-weight", "bold")
+ .text(group.title);
+
+
+ // Add legend items
+ const items = legendGroup.selectAll(".legend-item")
+ .data(group.items)
+ .enter()
+ .append("g")
+ .attr("class", "legend-item")
+ .attr("transform", (d, i) => `translate(0, ${i * itemSpacing + 50})`); // Increased spacing
+
+
+ // Add colored rectangles (slightly larger)
+ items.append("rect")
+ .attr("width", 55) // Increased from 50
+ .attr("height", 55) // Increased from 50
+ .attr("rx", 4)
+ .attr("fill", d => d.color);
+
+
+ // Add labels with larger font
+ items.append("text")
+ .attr("x", 75)
+ .attr("y", 40)
+ .attr("fill", "white")
+ .style("font-size", "44px") // Increased from 36px
+ .text(d => d.label);
+
+
+ // Update currentY for next group
+ currentY += (group.items.length * itemSpacing) + groupSpacing + 60;
+ });
+
+
+ } catch (error) {
+ console.error('Error loading CSV data:', error);
+ }
+ };
+
+
+ fetchData();
+}, [filters, onNodeSelect, onElementHover]);
+
+
+return (
+
+
+
+);
+};
+
+
+export default SankeyChart;
diff --git a/Homework3/vkosuri/React-Template/src/main.tsx b/Homework3/vkosuri/React-Template/src/main.tsx
new file mode 100644
index 0000000..87e2790
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import App from './App.tsx'
+import './style.css'
+
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+)
diff --git a/Homework3/vkosuri/React-Template/src/stores/Reducer.ts b/Homework3/vkosuri/React-Template/src/stores/Reducer.ts
new file mode 100644
index 0000000..0160617
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/src/stores/Reducer.ts
@@ -0,0 +1,25 @@
+// Define action types
+export const ACTIONS = { INCREMENT: 'increment' };
+
+interface Action{
+ type: string;
+}
+
+interface State{
+ count: number;
+}
+
+// Define initial state
+const initialState: State = { count: 0 };
+
+// reducer function
+export const reducer = (state: State, action: Action) => {
+ switch (action.type) {
+ case ACTIONS.INCREMENT:
+ return { count: state.count + 1 };
+ default:
+ return state;
+ }
+}
+
+export type { State, Action };
\ No newline at end of file
diff --git a/Homework3/vkosuri/React-Template/src/style.css b/Homework3/vkosuri/React-Template/src/style.css
new file mode 100644
index 0000000..f0dbf07
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/src/style.css
@@ -0,0 +1,280 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body, html {
+ background-color: #000;
+ color: #fff;
+ overflow: hidden;
+ font-family: 'Roboto', sans-serif;
+ height: 100%;
+}
+
+/* Dashboard container */
+.dashboard {
+ width: 100vw;
+ height: 100vh;
+ background-color: #000;
+ display: flex;
+ flex-direction: column;
+ padding: 5px;
+}
+
+/* Header - exactly 5% */
+.dashboard-header {
+ height: 5%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 5px;
+}
+
+.dashboard-header h1 {
+ font-size: 1.3rem;
+ color: #fff;
+ font-weight: normal;
+ white-space: nowrap;
+ text-align: center;
+ letter-spacing: 0.5px;
+ opacity: 0.95;
+}
+
+/* Active Filters Section */
+.active-filters {
+ display: flex;
+ gap: 10px;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: center;
+}
+
+.filter-tag {
+ display: inline-flex;
+ align-items: center;
+ background: rgba(255, 255, 255, 0.1);
+ padding: 4px 8px;
+ border-radius: 4px;
+ font-size: 0.8rem;
+}
+
+.filter-tag:hover {
+ background: rgba(255, 255, 255, 0.2);
+}
+
+.tag-label {
+ color: rgba(255, 255, 255, 0.7);
+ margin-right: 4px;
+}
+
+.tag-value {
+ font-weight: 500;
+}
+
+.remove-tag {
+ background: none;
+ border: none;
+ color: rgba(255, 255, 255, 0.6);
+ margin-left: 6px;
+ cursor: pointer;
+ padding: 0 4px;
+}
+
+.remove-tag:hover {
+ color: #fff;
+}
+
+/* Main layout */
+.dashboard-layout {
+ height: 95%;
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+}
+
+/* Top section - Sankey (61%) */
+.top-section {
+ height: 61%;
+ min-height: 61%;
+}
+
+.top-section .chart-container {
+ width: 100%;
+ height: 100%;
+ padding: 10px 20px;
+}
+
+/* Bottom section - Bar and Pie (34% total) */
+.bottom-section {
+ height: 34%;
+ display: flex;
+ gap: 5px;
+}
+
+/* Chart containers */
+.chart-container {
+ background-color: #000;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 4px;
+ padding: 10px;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+/* Half width for bottom charts */
+.half-width {
+ width: 50%;
+}
+
+/* Chart titles */
+.chart-container h2 {
+ font-size: 0.9rem;
+ color: #fff;
+ margin-bottom: 8px;
+ font-weight: normal;
+ white-space: nowrap;
+ opacity: 0.9;
+}
+
+/* Chart wrappers */
+.chart-wrapper {
+ flex: 1;
+ min-height: 0;
+ position: relative;
+}
+
+/* Specific wrapper styles */
+.sankey-wrapper {
+ width: 100%;
+ height: 100%;
+}
+
+.bar-wrapper {
+ height: 50% !important;
+ margin: auto 0;
+}
+
+.pie-wrapper {
+ height: 100%;
+}
+
+/* SVG styling */
+svg {
+ width: 100%;
+ height: 100%;
+ background-color: #000;
+}
+
+/* Navigation buttons */
+.back-to-overview {
+ background: rgba(255, 255, 255, 0.1);
+ border: none;
+ color: #fff;
+ padding: 6px 12px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 0.9rem;
+}
+
+.back-to-overview:hover {
+ background: rgba(255, 255, 255, 0.2);
+}
+
+/* Tooltip */
+.tooltip {
+ position: fixed;
+ background-color: rgba(0, 0, 0, 0.95);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ color: white;
+ padding: 12px;
+ border-radius: 6px;
+ pointer-events: none;
+ font-size: 0.8rem;
+ z-index: 1000;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+}
+
+.tooltip-header {
+ margin-bottom: 8px;
+ padding-bottom: 4px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.tooltip-body {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.stat-row {
+ display: flex;
+ justify-content: space-between;
+ gap: 20px;
+}
+
+/* Chart axis and text colors */
+.axis text {
+ fill: #fff;
+}
+
+.axis line, .axis path {
+ stroke: rgba(255, 255, 255, 0.2);
+}
+
+/* Legend styling */
+.legend text {
+ fill: #fff;
+ font-size: 0.8rem;
+}
+
+/* Sankey specific styles */
+.sankey-node text {
+ fill: #fff;
+ font-size: 0.8rem;
+}
+
+.sankey-link {
+ fill: none;
+ stroke-opacity: 0.3;
+ transition: stroke-opacity 0.3s ease;
+}
+
+/* Interactive elements */
+.interactive-element {
+ cursor: pointer;
+ transition: opacity 0.3s ease;
+}
+
+/* Transitions */
+.transitioning {
+ opacity: 0.5;
+ pointer-events: none;
+}
+
+/* Remove scrollbars */
+::-webkit-scrollbar {
+ display: none;
+}
+
+/* Responsive font sizes */
+@media (max-width: 1280px) {
+ .dashboard-header h1 {
+ font-size: 1.1rem;
+ }
+
+ .chart-container h2 {
+ font-size: 0.8rem;
+ }
+}
+
+@media (min-width: 1920px) {
+ .dashboard-header h1 {
+ font-size: 1.5rem;
+ }
+
+ .chart-container h2 {
+ font-size: 1rem;
+ }
+}
\ No newline at end of file
diff --git a/Homework3/vkosuri/React-Template/src/types.ts b/Homework3/vkosuri/React-Template/src/types.ts
new file mode 100644
index 0000000..bf460ce
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/src/types.ts
@@ -0,0 +1,232 @@
+// Base layout interfaces
+export interface Margin {
+ readonly left: number;
+ readonly right: number;
+ readonly top: number;
+ readonly bottom: number;
+}
+
+export interface ComponentSize {
+ width: number;
+ height: number;
+}
+
+export interface Point {
+ readonly posX: number;
+ readonly posY: number;
+}
+
+export interface Bar {
+ readonly value: number;
+}
+
+// Filter and Hover State Management
+export interface FilterState {
+ selectedAge: string | null;
+ selectedCondition: string | null;
+ selectedTreatment: string | null;
+}
+
+export interface HoverState {
+ element: {
+ type: 'node' | 'link' | 'bar' | 'pie';
+ category?: string;
+ name: string;
+ value: number;
+ percentage?: number;
+ coordinates: {
+ x: number;
+ y: number;
+ };
+ details?: Record;
+ } | null;
+}
+
+// Data Structures
+export interface StudentData {
+ Age: string;
+ "Choose your gender": string;
+ "What is your course?": string;
+ "Your current year of Study": string;
+ "What is your CGPA?": string;
+ "Do you have Depression?": string;
+ "Do you have Anxiety?": string;
+ "Do you have Panic attack?": string;
+ "Did you seek any specialist for a treatment?": string;
+}
+
+export interface ProcessedData {
+ sankeyData: {
+ nodes: NodeData[];
+ links: LinkData[];
+ };
+ barData: EnhancedBar[];
+ pieData: {
+ male: PieSegment[];
+ female: PieSegment[];
+ };
+ rawData: StudentData[];
+}
+
+// Sankey Chart Types
+export interface NodeData {
+ name: string;
+ category: 'age' | 'condition' | 'treatment';
+ index?: number;
+ x0?: number;
+ x1?: number;
+ y0?: number;
+ y1?: number;
+ value?: number;
+}
+
+export interface LinkData {
+ source: number | NodeData;
+ target: number | NodeData;
+ value: number;
+ width?: number;
+}
+
+// Bar Chart Types
+export interface EnhancedBar extends Bar {
+ category: string;
+ label: string;
+ color: string;
+ percentage: number;
+ condition?: string;
+}
+
+// Pie Chart Types
+export interface PieSegment {
+ id: string;
+ value: number;
+ label: string;
+ percentage: number;
+ color: string;
+}
+
+export interface GenderData {
+ male: MentalHealthCount[];
+ female: MentalHealthCount[];
+}
+
+export interface MentalHealthCount {
+ condition: string;
+ count: number;
+ percentage: number;
+}
+
+// Component Props
+export interface ChartProps {
+ filters: FilterState;
+ onElementHover: (element: HoverState['element'] | null, event: React.MouseEvent) => void;
+ isTransitioning: boolean;
+}
+
+export interface SankeyProps extends ChartProps {
+ onNodeSelect: (node: NodeData) => void;
+}
+
+// Color Schemes
+export interface ColorScheme {
+ age: {
+ [key: string]: string;
+ };
+ condition: {
+ [key: string]: string;
+ };
+ treatment: {
+ [key: string]: string;
+ };
+}
+
+// Chart Configuration
+export interface ChartDimensions extends ComponentSize {
+ margin: Margin;
+ boundedWidth: number;
+ boundedHeight: number;
+}
+
+export interface TooltipConfig {
+ position: Point;
+ content: {
+ title: string;
+ values: Array<{
+ label: string;
+ value: string | number;
+ }>;
+ };
+}
+
+// Animation Configuration
+export interface TransitionConfig {
+ duration: number;
+ ease: string;
+}
+
+// Event Handlers
+export interface ChartEventHandlers {
+ onHover?: (element: HoverState['element'] | null, event: React.MouseEvent) => void;
+ onClick?: (element: any) => void;
+ onMouseLeave?: () => void;
+}
+
+// Chart Scales
+export interface ChartScales {
+ xScale: any; // d3 scale
+ yScale: any; // d3 scale
+ colorScale: any; // d3 scale
+}
+
+// Legend Configuration
+export interface LegendConfig {
+ position: Point;
+ orientation: 'horizontal' | 'vertical';
+ items: Array<{
+ label: string;
+ color: string;
+ }>;
+}
+
+// Update Triggers
+export interface UpdateTriggers {
+ data?: boolean;
+ dimensions?: boolean;
+ scales?: boolean;
+ filters?: boolean;
+}
+
+// Accessibility Configuration
+export interface AccessibilityConfig {
+ title: string;
+ description: string;
+ ariaLabel: string;
+}
+
+// Data Processing Types
+export interface DataProcessor {
+ processData: (data: StudentData[]) => ProcessedData;
+ filterData: (data: ProcessedData, filters: FilterState) => ProcessedData;
+}
+
+// Chart State
+export interface ChartState {
+ data: ProcessedData | null;
+ dimensions: ChartDimensions | null;
+ scales: ChartScales | null;
+ isLoading: boolean;
+ error: string | null;
+}
+
+// Theme Configuration
+export interface ThemeConfig {
+ colors: ColorScheme;
+ fonts: {
+ primary: string;
+ secondary: string;
+ };
+ spacing: {
+ padding: number;
+ margin: number;
+ };
+}
\ No newline at end of file
diff --git a/Homework3/vkosuri/React-Template/src/vite-env.d.ts b/Homework3/vkosuri/React-Template/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/Homework3/vkosuri/React-Template/tsconfig.json b/Homework3/vkosuri/React-Template/tsconfig.json
new file mode 100644
index 0000000..3b32131
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2023", "DOM", "DOM.Iterable", "es6"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "noImplicitAny": true,
+ "noImplicitThis": true,
+ "strictNullChecks": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+ },
+ "include": ["src", "vite.config.ts"]
+}
diff --git a/Homework3/vkosuri/React-Template/vite.config.ts b/Homework3/vkosuri/React-Template/vite.config.ts
new file mode 100644
index 0000000..198ec17
--- /dev/null
+++ b/Homework3/vkosuri/React-Template/vite.config.ts
@@ -0,0 +1,10 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ server: {
+ port: 3000,
+ },
+ plugins: [react()],
+})