Untitled
unknown
plain_text
a year ago
21 kB
1
Indexable
Never
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;