-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
46e2ba8
commit 446f6e1
Showing
13 changed files
with
833 additions
and
128 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,124 +1,3 @@ | ||
"use client" | ||
|
||
|
||
import React, { useRef, useEffect, useState } from 'react'; | ||
import Boxplot from './components/boxplot'; | ||
import CurrentPrices from './components/currentprices'; | ||
import CurrentPricesMFLG from './components/currentprices-mflg'; | ||
import OptimisedPrices from './components/optimisedprice'; | ||
import DiscreteSlider from './components/slidebar'; // Import DiscreteSlider component | ||
import BoxPlotLegend from './components/boxplot-legend'; | ||
import './pricebar.css'; | ||
|
||
interface ProductData { | ||
prices: number[]; | ||
product_price: number; | ||
ranking: number; | ||
similar: number[]; | ||
} | ||
|
||
function extractSimilarPrices(data: ProductData): { [key: string]: number } { | ||
const prices: number[] = data.prices; | ||
const similarIndices: number[] = data.similar; | ||
const productPrice: number = data.product_price; | ||
const extractedData: { [key: string]: number } = {}; | ||
|
||
// Loop through similar indices and extract corresponding prices | ||
for (let i = 0; i < prices.length; i++) { | ||
extractedData[similarIndices[i].toString()] = prices[i]; | ||
}; | ||
|
||
// Add the product price under the key 'mflg' | ||
extractedData['mflg'] = productPrice; | ||
|
||
|
||
return extractedData; | ||
} | ||
|
||
|
||
|
||
const PriceBar = () => { | ||
const sample_similar_products: ProductData = { | ||
"prices": [10.0, 12.0, 13.0, 24.5, 26.0, 40.0], | ||
"product_price": 25.0, | ||
"ranking": 0.82, | ||
"similar": [3, 5, 6, 20, 35, 49] | ||
}; | ||
|
||
const extractedData = extractSimilarPrices(sample_similar_products); | ||
|
||
const svgRef = useRef<SVGSVGElement>(null); | ||
// const [data,setData] = useState({ e: 10, a: 30, mflg: 40, z:40, aw:43.95, y:40, x:40, f: 53, g: 70, az:53, i: 90, j: 200 , ah:92.5, ab:90, ac:90, ad:200, ax:51}); | ||
const [data,setData] = useState(extractedData); | ||
const [newData, setNewData] = useState({ k: 50 }); // State for newData | ||
const [sliderValue, setSliderValue] = useState(50);// State for newData | ||
const width = 500; | ||
const height = 150; | ||
const legendItems = [ | ||
{ label: 'MFLG product optimised price', color: 'red' }, | ||
{ label: 'MFLG product current price', color: 'green' }, | ||
{ label: 'Competitor product current price', color: 'steelblue' }, | ||
]; | ||
|
||
const handleSliderChange = (newValue: number) => { | ||
setSliderValue(newValue); | ||
}; | ||
|
||
const handleUpdateData = () => { | ||
|
||
setData({}); | ||
setNewData({}); | ||
|
||
const svg = svgRef.current; | ||
if (svg) { | ||
while (svg.firstChild) { | ||
svg.removeChild(svg.firstChild); | ||
} | ||
} | ||
setData(extractedData); | ||
// setData({ e: 10, a: 30, mflg: 40, z:40, aw:43.95, y:40, x:40, f: 53, g: 70, az:53, i: 90, j: 200 , ah:92.5, ab:90, ac:90, ad:200, ax:51}); | ||
setNewData({ k: sliderValue }) | ||
}; | ||
|
||
// const handleSliderChange = (newValue: number) => { | ||
// setSliderValue(newValue); | ||
|
||
|
||
// setData({}); | ||
// setNewData({}); | ||
|
||
// const svg = svgRef.current; | ||
// if (svg) { | ||
// while (svg.firstChild) { | ||
// svg.removeChild(svg.firstChild); | ||
// } | ||
// } | ||
// setData(extractedData); | ||
// // setData({ e: 10, a: 30, mflg: 40, z:40, aw:43.95, y:40, x:40, f: 53, g: 70, az:53, i: 90, j: 200 , ah:92.5, ab:90, ac:90, ad:200, ax:51}); | ||
// setNewData({ k: newValue }) | ||
// }; | ||
|
||
|
||
return ( | ||
<div className="container"> | ||
<div className="boxplot-container"> | ||
<div className="content"> | ||
<div className="prices"> | ||
<h5>Slide to choose percentile</h5> | ||
<div className="slider-button-container"> | ||
<DiscreteSlider handleChange={handleSliderChange} /> | ||
<button onClick={handleUpdateData}>Show optimised price</button> | ||
</div> | ||
<Boxplot data={data} svgRef={svgRef} width={width} height={height} /> | ||
<CurrentPrices data={data} svgRef={svgRef} width={width} height={height} /> | ||
<CurrentPricesMFLG data={data} svgRef={svgRef} width={width} height={height} /> | ||
<OptimisedPrices data={data} newData={newData} svgRef={svgRef} width={width} height={height}/> | ||
<BoxPlotLegend legendItems={legendItems} /> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export default PriceBar; | ||
export default function Empty () { | ||
return <h1>Empty!</h1> | ||
} |
22 changes: 22 additions & 0 deletions
22
webapp/src/app/(authenticated)/pricebar/components/boxplot-legend.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
.legend { | ||
display: flex; | ||
flex-direction: column; | ||
} | ||
|
||
.legend-item { | ||
display: flex; | ||
align-items: center; | ||
margin-bottom: 5px; | ||
} | ||
|
||
.circle { | ||
width: 10px; | ||
height: 10px; | ||
border-radius: 50%; | ||
margin-right: 5px; | ||
} | ||
|
||
.label { | ||
font-size: 14px; | ||
} | ||
|
27 changes: 27 additions & 0 deletions
27
webapp/src/app/(authenticated)/pricebar/components/boxplot-legend.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import React from 'react'; | ||
import './boxplot-legend.css'; | ||
|
||
|
||
interface LegendItem { | ||
label: string; | ||
color: string; | ||
} | ||
|
||
interface BoxPlotLegendProps { | ||
legendItems: LegendItem[]; | ||
} | ||
|
||
const BoxPlotLegend: React.FC<BoxPlotLegendProps> = ({ legendItems }) => { | ||
return ( | ||
<div className="legend"> | ||
{legendItems.map((item, index) => ( | ||
<div key={index} className="legend-item"> | ||
<span className="circle" style={{ backgroundColor: item.color }}></span> | ||
<span className="label">{item.label}</span> | ||
</div> | ||
))} | ||
</div> | ||
); | ||
}; | ||
|
||
export default BoxPlotLegend; |
141 changes: 141 additions & 0 deletions
141
webapp/src/app/(authenticated)/pricebar/components/boxplot.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
"use client" | ||
|
||
import React, { useEffect, useRef } from 'react'; | ||
import * as d3 from 'd3'; | ||
|
||
interface BoxPlotProps { | ||
data: { [key: string]: number }; | ||
width: number; | ||
height: number; | ||
svgRef: React.MutableRefObject<SVGSVGElement | null>; | ||
} | ||
|
||
const BoxPlot: React.FC<BoxPlotProps> = ({ data, width, height, svgRef }) => { | ||
useEffect(() => { | ||
if (!svgRef.current) return; | ||
|
||
const dataArray = Object.values(data); | ||
if (dataArray.length === 0) return; | ||
|
||
const margin = { top: 20, right: 50, bottom: 50, left: 2 }; | ||
const innerWidth = width - margin.left - margin.right; | ||
const innerHeight = height - margin.top - margin.bottom; | ||
|
||
const svg = d3.select(svgRef.current) | ||
.attr('width', width) | ||
.attr('height', height) | ||
.append('g') | ||
.attr('transform', `translate(${margin.left}, ${margin.top})`); | ||
|
||
const yScale = d3.scaleBand() | ||
.domain(['Box Plot']) | ||
.range([margin.top, innerHeight]) | ||
.padding(0.1); | ||
|
||
const xScale = d3.scaleLinear() | ||
.domain([d3.min(dataArray)!, d3.max(dataArray)!]) | ||
.nice() | ||
.range([margin.left, innerWidth]); | ||
|
||
const g = svg.append('g') | ||
.attr('transform', `translate(${margin.left},${margin.top})`); | ||
|
||
// Draw box plot | ||
const boxPlotGroup = g.append('g'); | ||
|
||
const boxHeight = yScale.bandwidth(); | ||
|
||
Object.entries(data).forEach(([key, value]) => { | ||
// For each key-value pair in the data object | ||
boxPlotGroup.append('rect') | ||
.attr('y', yScale('Box Plot') - 20) | ||
.attr('x', xScale(d3.quantile(dataArray, 0.25))) | ||
.attr('height', 40) | ||
.attr('width', xScale(d3.quantile(dataArray, 0.75)) - xScale(d3.quantile(dataArray, 0.25))) | ||
.attr('fill', 'rgba(0, 0, 0, 0.01)'); | ||
}); | ||
|
||
|
||
boxPlotGroup.append('line') | ||
.attr('y1', yScale('Box Plot') - 20) | ||
.attr('y2', yScale('Box Plot') + 20) | ||
.attr('x1', xScale(d3.quantile(dataArray, 0.5))) | ||
.attr('x2', xScale(d3.quantile(dataArray, 0.5))) | ||
.attr('stroke', 'black'); | ||
|
||
boxPlotGroup.append('line') | ||
.attr('y1', yScale('Box Plot') - 20) | ||
.attr('y2', yScale('Box Plot') + 20) | ||
.attr('x1', xScale(d3.min(dataArray)!)) | ||
.attr('x2', xScale(d3.min(dataArray)!)) | ||
.attr('stroke', 'black') | ||
.attr('stroke-width', 0.5) | ||
.attr('stroke-dasharray', '3'); | ||
|
||
boxPlotGroup.append('line') | ||
.attr('y1', yScale('Box Plot') - 20) | ||
.attr('y2', yScale('Box Plot') + 20) | ||
.attr('x1', xScale(d3.max(dataArray)!)) | ||
.attr('x2', xScale(d3.max(dataArray)!)) | ||
.attr('stroke', 'black') | ||
.attr('stroke-width', 0.5) | ||
.attr('stroke-dasharray', '3'); | ||
|
||
boxPlotGroup.append('line') | ||
.attr('y1', yScale('Box Plot') - 20) | ||
.attr('y2', yScale('Box Plot') + 20) | ||
.attr('x1', xScale(d3.quantile(dataArray, 0.25))) | ||
.attr('x2', xScale(d3.quantile(dataArray, 0.25))) | ||
.attr('stroke', 'black') | ||
|
||
|
||
boxPlotGroup.append('line') | ||
.attr('y1', yScale('Box Plot') - 20) | ||
.attr('y2', yScale('Box Plot') + 20) | ||
.attr('x1', xScale(d3.quantile(dataArray, 0.75))) | ||
.attr('x2', xScale(d3.quantile(dataArray, 0.75))) | ||
.attr('stroke', 'black') | ||
|
||
boxPlotGroup.append('line') | ||
.attr('y1', yScale('Box Plot')) | ||
.attr('y2', yScale('Box Plot')) | ||
.attr('x1', xScale(d3.min(dataArray)!)) | ||
.attr('x2', xScale(d3.quantile(dataArray, 0.25))) | ||
.attr('stroke', 'black') | ||
|
||
boxPlotGroup.append('line') | ||
.attr('y1', yScale('Box Plot')) | ||
.attr('y2', yScale('Box Plot')) | ||
.attr('x1', xScale(d3.quantile(dataArray, 0.75))) | ||
.attr('x2', xScale(d3.max(dataArray)!)) | ||
.attr('stroke', 'black') | ||
|
||
boxPlotGroup.append('line') | ||
.attr('y1', yScale('Box Plot')-20) | ||
.attr('y2', yScale('Box Plot')-20) | ||
.attr('x1', xScale(d3.quantile(dataArray, 0.25))) | ||
.attr('x2', xScale(d3.quantile(dataArray, 0.75))) | ||
.attr('stroke', 'black') | ||
|
||
boxPlotGroup.append('line') | ||
.attr('y1', yScale('Box Plot')+20) | ||
.attr('y2', yScale('Box Plot')+20) | ||
.attr('x1', xScale(d3.quantile(dataArray, 0.25))) | ||
.attr('x2', xScale(d3.quantile(dataArray, 0.75))) | ||
.attr('stroke', 'black') | ||
|
||
const xAxis = d3.axisBottom(xScale); | ||
|
||
g.append('g') | ||
.attr('class', 'x-axis') | ||
.call(xAxis) | ||
.attr("transform", `translate(0, ${innerHeight})`); | ||
|
||
}, [data, width, height, svgRef]); | ||
|
||
return ( | ||
<svg ref={svgRef}></svg> | ||
); | ||
} | ||
|
||
export default BoxPlot; |
Oops, something went wrong.