import React, { useEffect, useRef, useState } from "react";
import ReactECharts from "echarts-for-react";
import MaskedDate from "../custom-components/MaskedDate";
import Button from "../custom-components/Button";
import { KeyCodes } from "renderer/util/keycodes";
import { Dropdown } from "primereact/dropdown";
import { TreeSelect } from 'primereact/treeselect';
import { checkDateIsSameOrAfter } from "renderer/util/validator/globalValidator";
import { useLazyGetOrganisationDataByIdQuery } from "renderer/store/createOrganization/organizationApi";
import { getSelectedOrgId } from "renderer/util/miscUtil";
import { toast } from "react-toastify";
import CustomLoader from "../custom-components/CustomLoader";
// import echarts from 'echarts';
const CreateChart = ({ data, el, i, edit, onQuestionClick = () => { } }) => {
const [filters, setFilters] = useState([])
const filt = useRef([])
const [result, setResult] = useState()
const [showRevert, setShowRevert] = useState(false)
const currentOrgID = getSelectedOrgId();
const [getOrganisationDetails] = useLazyGetOrganisationDataByIdQuery()
const [organizationData, setOrganizationData] = useState({})
const [options, setOptions] = useState({})
const [showLoader, setShowLoader] = useState(true)
useEffect(() => {
let opts = { ...options }
if (Object.keys(opts).length > 0) {
if (el.w > 6) {
opts.yAxis = opts?.yAxis && { ...opts?.yAxis, show: true }
opts.xAxis = opts?.xAxis && { ...opts?.xAxis, show: true }
opts.grid = { top: 2, right: "4%", bottom: 24, left: 2, containLabel: true }
} else {
opts.yAxis = opts?.yAxis && { ...opts?.yAxis, show: false }
opts.xAxis = opts?.xAxis && { ...opts?.xAxis, show: false }
opts.grid = { top: 2, right: "4%", bottom: 24, left: 36, containLabel: false }
}
}
setOptions(opts)
setShowRevert(el?.parentQuestionId > 0 ? true : false)
}, [el])
const chartStyle = {
height: el.h * 31,
width: "100%",
padding: "10px 10px 0",
};
const chartRef = useRef(null);
const apiCall = (data) => {
const options = {
method: 'POST',
headers: {
"X-Organization-ID": getSelectedOrgId()
},
body: data.payload
};
fetch(data.endPoint, options)
.then(response => response.json())
.then(qdata => {
// Process the returned data
const { results } = qdata
const currentChart = data.charts.filter(e => e.chartType === data.defaultView)[0]
const defaultX = currentChart.xAxisList.filter(e => e.default)[0]
const defaultY = currentChart.yAxisList.filter(e => e.default)[0]
setResult(results)
let xaxisData = []
let yaxisData = []
results?.forEach(e => {
yaxisData.push(e[`${defaultY?.key}`])
xaxisData.push(e[`${defaultX?.key}`])
})
if (data.defaultView === 'Pie') {
let xAxis; let yAxis;
data.charts.forEach((obj) => {
if (obj.chartType === "Pie") {
xAxis = obj?.xAxisList?.find(ele => ele.default)
yAxis = obj?.yAxisList?.find(ele => ele.default)
}
})
let pieData = results.map((item) => {
return {
value: item[`${yAxis?.key}`],
name: item[`${xAxis?.key}`],
}
})
setOptions({
grid: el.w > 6 ? { top: 2, right: "4%", bottom: 24, left: 2, containLabel: true } : { top: 2, right: "4%", bottom: 24, left: 2, containLabel: false },
series: [
{
type: data.defaultView?.toLowerCase() ?? "pie",
data: pieData
}
]
})
} else if (data.defaultView === 'Tree Map') {
let xAxis; let yAxis;
data.charts.forEach((obj) => {
if (obj.chartType === "Tree Map") {
xAxis = obj?.xAxisList?.find(ele => ele.default)
yAxis = obj?.yAxisList?.find(ele => ele.default)
}
})
const treemapData = {
name: 'Root',
children: results?.map((item) => ({
name: item[`${xAxis?.key}`],
value: item[`${yAxis?.key}`]
}))
};
setOptions({
grid: el.w > 6 ? { top: 2, right: "4%", bottom: 24, left: 2, containLabel: true } : { top: 2, right: "4%", bottom: 24, left: 2, containLabel: false },
series: [
{
type: 'treemap',
data: [treemapData],
label: {
show: true,
},
},
],
tooltip: {}
})
} else if (data.defaultView === "Dough Nut") {
let xAxis; let yAxis;
data.charts.forEach((obj) => {
if (obj.chartType === "Dough Nut") {
xAxis = obj?.xAxisList?.find(ele => ele.default)
yAxis = obj?.yAxisList?.find(ele => ele.default)
}
})
const donutData = results?.map((item) => ({
name: item[`${xAxis?.key}`],
value: item[`${yAxis?.key}`]
}))
setOptions({
grid: el.w > 6 ? { top: 2, right: "4%", bottom: 24, left: 2, containLabel: true } : { top: 2, right: "4%", bottom: 24, left: 2, containLabel: false },
series: [
{
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
},
emphasis: {
label: {
show: true,
fontSize: 20,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: donutData
}],
tooltip: {}
})
} else if (data.defaultView === 'Scatter') {
let xAxis; let yAxis;
data.charts.forEach((obj) => {
if (obj.chartType === "Scatter") {
xAxis = obj?.xAxisList?.find(ele => ele.default)
yAxis = obj?.yAxisList?.find(ele => ele.default)
}
})
const xAxisData = results?.map(item => item[`${xAxis?.key}`])
const yAxisData = results?.map(item => item[`${yAxis?.key}`]);
setOptions({
grid: el.w > 6 ? { top: 2, right: "4%", bottom: 24, left: 2, containLabel: true } : { top: 2, right: "4%", bottom: 24, left: 2, containLabel: false },
xAxis: {
data: xAxisData
},
yAxis: {},
series: [
{
type: data?.defaultView?.toLowerCase() ?? 'scatter',
data: yAxisData
}
],
tooltip: {
trigger: "axis",
position: 'top'
}
})
} else if (data.defaultView === 'Bubble') {
let xAxis; let yAxis;
data.charts.forEach((obj) => {
if (obj.chartType === "Bubble") {
xAxis = obj?.xAxisList?.find(ele => ele.default)
yAxis = obj?.yAxisList?.find(ele => ele.default)
}
})
const { tooltip, ...rest } = options
let d = []
d = results?.map(e => {
return [e['ledgerId'], e[`${yAxis?.key}`], e[`${xAxis?.key}`]];
});
setOptions({
...rest,
grid: el.w > 6 ? { top: 2, right: "4%", bottom: 24, left: 2, containLabel: true } : { top: 2, right: "4%", bottom: 24, left: 2, containLabel: false },
xAxis: {
splitLine: {
lineStyle: {
type: 'dashed'
}
},
scale: true
},
yAxis: {
splitLine: {
lineStyle: {
type: 'dashed'
},
},
scale: true
},
series: [
{
name: '',
data: d,
type: 'scatter',
symbolSize: function (d) {
return Math.sqrt(d[1]) / 25; // Adjust the divisor (scaling factor)
},
emphasis: {
focus: 'series',
label: {
show: true,
formatter: function (param) {
return param.data[2];
},
position: 'bottom'
}
},
}
]
})
} else if (data.defaultView === 'Column Bar' || data.defaultView === 'Line') {
setOptions({
grid: el.w > 6 ? { top: 2, right: "4%", bottom: 24, left: 2, containLabel: true } : { top: 2, right: "4%", bottom: 24, left: 2, containLabel: false },
xAxis: {
show: el.w > 6,
type: "category",
// boundaryGap: [0, 0.001],
data: xaxisData,
},
yAxis: {
show: el.w > 6,
type: "value",
},
series: [
{
// name: '2021-22',
type: data.defaultView?.toLowerCase() === 'column bar' ? 'bar' : data.defaultView?.toLowerCase(),
data: yaxisData
}
],
tooltip: {
trigger: "axis",
position: 'top'
}
})
} else if (data.defaultView === "Area") {
setOptions({
grid: el.w > 6 ? { top: 2, right: "4%", bottom: 24, left: 2, containLabel: true } : { top: 2, right: "4%", bottom: 24, left: 2, containLabel: false },
xAxis: {
show: el.w > 6,
type: "category",
data: xaxisData,
},
yAxis: {
show: el.w > 6,
type: "value",
},
series: [
{
// name: '2021-22',
type: "line",
data: yaxisData,
areaStyle: {}
}
],
})
} else {
setOptions({
grid: el.w > 6 ? { top: 2, right: "4%", bottom: 24, left: 2, containLabel: true } : { top: 2, right: "4%", bottom: 24, left: 2, containLabel: false },
xAxis: {
show: el.w > 6,
type: 'value',
// boundaryGap: [0, 0.001],
},
yAxis: {
show: el.w > 6,
type: "category",
data: xaxisData,
},
series: [
{
// name: '2021-22',
type: "bar",
data: yaxisData
}
],
tooltip: {
trigger: "axis",
position: 'top'
}
})
}
if(data.endPoint!=='https://devapi.finsights.biz/kitaabreportsapi/v1/dashboard/TopExpensesAggregatesReportByLedgerId'){
setShowLoader(false)
} else {
setShowLoader(true)
}
})
.catch(error => {
// Handle any errors
console.error('Error:', error);
});
}
useEffect(() => {
if (data) {
fetchOrganisationDetails();
let payload = JSON.parse(data.payload)
const updatedFilters = data.filters.map((element) => {
if (element.payloadKey) {
return {
...element, value: payload[`${element.payloadKey}`]
}
} else {
return { ...element, value: '' }
}
});
filt.current = updatedFilters
setFilters(updatedFilters)
if(data?.reportName!==60001) {
apiCall(data)
}
const dynamicElements = data?.filters.filter((element) => element?.status === 'dynamic');
dynamicElements?.forEach((element) => dynamicOptions(element));
}
}, [data])
const getOptions = (options) => {
let flatten = [];
if (options?.length > 1) {
options.forEach((option) => {
flatten.push({
key: option?.Id.toString(),
label: option?.Name,
data: option?.Name,
icon: "",
children: getOptions(option.Children),
})
});
}
return flatten;
};
const dynamicOptions = (data) => {
const options = {
method: 'POST',
headers: {
"X-Organization-ID": getSelectedOrgId()
},
body: data.payload
};
fetch(data.endPoint, options)
.then(response => response.json())
.then(qdata => {
// Process the returned data
const results = getOptions(qdata?.results)
setFilters(prevFilters => {
const updatedFilters = prevFilters.map((element) => {
if (element?.status === 'dynamic') {
return {
...element,
options: results
};
}
return element;
});
filt.current = updatedFilters
return updatedFilters;
});
})
}
const fetchOrganisationDetails = async () => {
try {
const resp = await getOrganisationDetails(currentOrgID);
if (resp?.data?.success) {
setOrganizationData(resp?.data)
}
}
catch (error) {
console.log(error);
}
}
const handleChange = (e, ele) => {
ele.value = e.target.value
}
const handleMouseDown = () => {
}
const submitHandler = (e, ele, filters) => {
const updatedData = { ...data };
if (!updatedData["defaultPayload"]) {
updatedData["defaultPayload"] = updatedData.payload;
}
let shouldCallApi = true;
let parsedPayload = JSON.parse(updatedData.payload);
filt.current?.forEach((element) => {
if (!shouldCallApi) return;
if (element.type === "Date") {
if (element.payloadKey && element.payloadKey.trim() !== '') {
if (element.payloadKey === 'fromDate') {
const isDateValid = checkDateIsSameOrAfter(element.value, organizationData?.data?.preferences?.booksBeginningFrom)
if (isDateValid) {
parsedPayload[`${element.payloadKey}`] = element.value;
} else {
toast.error('From Date should not be earlier than Books Beginning Date')
shouldCallApi = false;
return
}
} else {
parsedPayload[`${element.payloadKey}`] = element.value;
}
}
} else if (element.type === "Dropdown") {
if (element.status === 'dynamic') {
const selectedOption = findTreeNodeByKey(element?.value, element?.options); // as treeselect selection based on "key"
if (selectedOption) {
parsedPayload[`${element.payloadKey}`] = selectedOption?.data;
}
} else {
if (element.payloadKey && element.payloadKey.trim() !== '') {
parsedPayload[`${element.payloadKey}`] = element.value;
}
}
}
});
if (shouldCallApi) {
updatedData.payload = JSON.stringify(parsedPayload);
apiCall(updatedData);
}
};
const findTreeNodeByKey = (key, nodes) => {
for (const node of nodes) {
if (node.key === key) {
return node;
}
if (node.children?.length) {
const foundNode = findTreeNodeByKey(key, node.children);
if (foundNode) {
return foundNode;
}
}
}
return null;
};
const handleChangeDate = (e, i) => {
let flt = [...filt.current]
flt[i].value = e.target.value
filt.current = flt
}
const onChangeDropDown = (e, i) => {
let flt = [...filters]
flt[i].value = e.value
filt.current = flt
setFilters(flt)
}
const renderFilters = filt.current?.map((ele, i) => (
<div className="me-2">
{ele?.type && ele?.type?.toLowerCase() === "date" && <MaskedDate
onMouseUp={handleMouseDown}
id="VCHC4"
value={ele.value}
name={"entryDate"}
onChange={(e) => handleChangeDate(e, i)}
placeholder={ele.label}
className="form-control h-40px f-c-b bg-white"
/>}
{ele?.type && ele?.type?.toLowerCase() === "dropdown" && ele?.status === 'static' && <div className="p-rel select-inp">
<span className="p-input-icon-rem w-100">
<Dropdown
className="shadow-none"
value={ele.value}
options={ele.options}
optionValue="label"
optionLabel="label"
onChange={(e) => onChangeDropDown(e, i)}
panelStyle={{ maxWidth: "250px" }}
filter
filterBy="label"
placeholder="Select Type"
/>
</span>
</div>
}
{ele?.type && ele?.type?.toLowerCase() === "dropdown" && ele?.status === 'dynamic' && <div className="tree-inp">
<TreeSelect
value={ele.value}
options={ele?.options}
selectionMode="single"
onChange={(e) => onChangeDropDown(e, i)}
placeholder="Select Type"
/></div>
}
{ele?.type && ele?.type?.toLowerCase() === "button" &&
<button
className="btn btn-primary btn-sm"
onClick={(e) =>
submitHandler(e, ele, filters)
}
>
{ele.label}
</button>
}
</div>
));
const handleBarClick = (params) => {
const d = { ...data }
let clickedLedger = {}
if (d.defaultView === "Bubble") {
clickedLedger = result?.find(item => {
return Object.values(item).some(e => params.value?.includes(e))
})
} else {
clickedLedger = result?.find(item => Object.values(item).includes(params?.name) && Object.values(item).includes(params.value));
}
// to find top expenses aggregate by ledger id of top expenses
if (clickedLedger && d?.reportName === 4) {
const payloadObj = JSON.parse(d.payload);
payloadObj.ledgerId = clickedLedger?.ledgerId;
d.payload = JSON.stringify(payloadObj);
d.endPoint = 'https://devapi.finsights.biz/kitaabreportsapi/v1/dashboard/TopExpensesAggregatesReportByLedgerId'
apiCall(d);
d['questionId'] = (el?.childQuestionId !== undefined && el?.childQuestionId !== 0) ? el?.childQuestionId : 60001;
setShowLoader(true)
onQuestionClick(d, 'top expenses', el, setShowLoader);
}
};
const revertQuestionId = () => {
const d = { ...data }
d['questionId'] = el?.parentQuestionId;
setShowLoader(true)
onQuestionClick(d, 'top expenses', el, setShowLoader);
}
return (
<>
<div className="chart-container" style={!edit ? { overflow: "hidden auto", height: el.h * 38 } : { overflow: "hidden auto", height: el.h * 38, pointerEvents: 'none' }}>
<div className="card rounded-0 border-0">
<div className="card-header p-15-total figma-bg bottom-color dashed-color-card-header">
{/* remove above dashed-color-card-header*/}
<div className="row align-items-center">
<div className="col-xxl-4 col-xl-4 col-lg-4 col-md-4 col-sm-4">
<p className="font-size-14 f-500 mb-0">
{el?.question ?? data?.question}
</p>
</div>
<div className="col-xxl-8 col-xl-8 col-lg-8 col-md-8 col-sm-8 text-end">
<div className="d-flex justify-content-end">
{renderFilters}
{showRevert &&
<button
className="btn btn-success btn-sm"
onClick={revertQuestionId}
>Revert
</button>
}
</div>
</div>
</div>
</div>
<div className="card-body p-15-total">
<div className="row">
{showLoader ? <CustomLoader load={true} /> :
<div className="col-md-12" style={{ overflow: "auto" }}>
<ReactECharts ref={chartRef} option={options} style={chartStyle} onEvents={{ click: handleBarClick }} />
</div>
}
</div>
</div>
</div>
</div>
</>
);
}
export default CreateChart;