at the end of the day, it was inevitable
This commit is contained in:
+357
@@ -0,0 +1,357 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Col, Row } from 'reactstrap';
|
||||
import ECharts from '../../../../../common/charts/ECharts';
|
||||
import ChartWrapper from '../ChartWrapper';
|
||||
import {
|
||||
getBarOptions,
|
||||
getPieOptions
|
||||
} from '../../../../../common/charts/ChartsOptions';
|
||||
import { IoIosAdd, IoIosRefresh, IoIosCheckmark } from 'react-icons/io';
|
||||
import reduxConnect from '../../../../../../redux/utils/connect';
|
||||
import translate from 'react-i18next/dist/commonjs/translate';
|
||||
import { compose } from 'redux';
|
||||
import { getOverviewPieAPI } from '../../../../../../api/analytics/createAnalytics';
|
||||
import useIsMounted from '../../../../../common/hooks/useIsMounted';
|
||||
|
||||
const initialBar = {
|
||||
data: [],
|
||||
error: undefined,
|
||||
loading: true,
|
||||
vertical: false
|
||||
};
|
||||
const initialPie = { data: [], error: undefined, loading: true };
|
||||
|
||||
function Demographics(props) {
|
||||
const { actions, analyze, feedData, id, t } = props;
|
||||
const isMounted = useIsMounted();
|
||||
const [barCountriesData, setBarCountriesData] = useState(initialBar);
|
||||
const [barLanguagesData, setBarLanguagesData] = useState(initialBar);
|
||||
const [genderData, setGenderData] = useState(initialPie);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
// getCountriesData()
|
||||
getLanguagesData();
|
||||
getGenderData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (barCountriesData.data) {
|
||||
setBarCountriesData((prev) => ({
|
||||
...prev,
|
||||
data: {
|
||||
...prev.data,
|
||||
xAxis: prev.data.yAxis,
|
||||
yAxis: prev.data.xAxis
|
||||
}
|
||||
}));
|
||||
}
|
||||
}, [barCountriesData.vertical]);
|
||||
|
||||
useEffect(() => {
|
||||
if (barLanguagesData.data) {
|
||||
setBarLanguagesData((prev) => ({
|
||||
...prev,
|
||||
data: {
|
||||
...prev.data,
|
||||
xAxis: prev.data.yAxis,
|
||||
yAxis: prev.data.xAxis
|
||||
}
|
||||
}));
|
||||
}
|
||||
}, [barLanguagesData.vertical]);
|
||||
|
||||
function updateResult(foo, id) {
|
||||
switch (id) {
|
||||
case cn.first:
|
||||
// getCountriesData()
|
||||
return;
|
||||
case cn.second:
|
||||
getLanguagesData();
|
||||
return;
|
||||
case cn.third:
|
||||
getGenderData();
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Uncomment when country chart shows up
|
||||
function getCountriesData() {
|
||||
setBarCountriesData((prev) => ({ ...prev, loading: true }))
|
||||
getOverviewPieAPI('country', id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// on error
|
||||
setBarCountriesData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}))
|
||||
return
|
||||
}
|
||||
const { data } = res.data
|
||||
const barOptions = {}
|
||||
const errors = {}
|
||||
Object.entries(data).forEach((feed) => {
|
||||
const [name, value] = feed
|
||||
const labels = ['Results']
|
||||
const datasets = Object.keys(value).map((item) => ({
|
||||
name: item,
|
||||
type: 'bar',
|
||||
data: [value[item]]
|
||||
}))
|
||||
|
||||
if (!datasets || (Array.isArray(datasets) && datasets.length < 1)) {
|
||||
errors[name] = t('analyzeTab.noData');
|
||||
}
|
||||
|
||||
barOptions[name] = getBarOptions(datasets, labels)
|
||||
})
|
||||
|
||||
setBarCountriesData({
|
||||
data: barOptions,
|
||||
error: errors,
|
||||
loading: false,
|
||||
vertical: false
|
||||
})
|
||||
})
|
||||
} */
|
||||
|
||||
function getLanguagesData() {
|
||||
setBarLanguagesData((prev) => ({ ...prev, loading: true }));
|
||||
getOverviewPieAPI('language', id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// on error
|
||||
setBarLanguagesData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
const { data } = res.data;
|
||||
const barOptions = {};
|
||||
const errors = {};
|
||||
Object.entries(data).forEach((feed) => {
|
||||
const [name, value] = feed;
|
||||
const labels = ['Results'];
|
||||
const datasets = Object.keys(value).map((item) => ({
|
||||
name: item,
|
||||
type: 'bar',
|
||||
data: [value[item]]
|
||||
}));
|
||||
|
||||
if (!datasets || (Array.isArray(datasets) && datasets.length < 1)) {
|
||||
errors[name] = t('analyzeTab.noData');
|
||||
}
|
||||
|
||||
barOptions[name] = getBarOptions(datasets, labels);
|
||||
});
|
||||
|
||||
setBarLanguagesData({
|
||||
data: barOptions,
|
||||
error: errors,
|
||||
loading: false,
|
||||
vertical: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getGenderData() {
|
||||
setGenderData((prev) => ({ ...prev, loading: true }));
|
||||
getOverviewPieAPI('gender', id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// on error
|
||||
setGenderData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = res.data;
|
||||
const pieOptions = {};
|
||||
const errors = {};
|
||||
|
||||
Object.entries(data).forEach((feed) => {
|
||||
const [name, value] = feed;
|
||||
|
||||
if (!value || (Array.isArray(value) && value.length < 1)) {
|
||||
errors[name] = t('analyzeTab.noData');
|
||||
}
|
||||
|
||||
pieOptions[name] = getPieOptions(
|
||||
Object.entries(value).map((v) => ({
|
||||
name: v[0],
|
||||
value: v[1]
|
||||
}))
|
||||
);
|
||||
});
|
||||
|
||||
setGenderData({
|
||||
data: pieOptions,
|
||||
error: errors,
|
||||
loading: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function changeVertical(name, id) {
|
||||
name === cn.first
|
||||
? setBarCountriesData((prev) => ({ ...prev, vertical: !prev.vertical }))
|
||||
: setBarLanguagesData((prev) => ({ ...prev, vertical: !prev.vertical }));
|
||||
}
|
||||
|
||||
const hideChartAlert = (name, id) =>
|
||||
analyze.alertCharts.find((v) => v.name === name && v.id === id);
|
||||
const hideChartPieAlert = (id) =>
|
||||
analyze.alertCharts.find((v) => v.name === cn.third && v.id === id);
|
||||
|
||||
const barchartMenus = (name, id) => [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: name, id }),
|
||||
showInMore: false,
|
||||
hide: hideChartAlert(name, id)
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChartAlert(name, id)
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, name),
|
||||
showInMore: false
|
||||
},
|
||||
/* {
|
||||
title: t('analyzeTab.chartMenus.addToDashboard'),
|
||||
fn: () => {},
|
||||
showInMore: true
|
||||
}, */
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.toggleHV'),
|
||||
fn: () => changeVertical(name, id),
|
||||
showInMore: true
|
||||
}
|
||||
];
|
||||
|
||||
const piechartMenus = (id) => [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: cn.third, id }),
|
||||
showInMore: false,
|
||||
hide: hideChartPieAlert(id)
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChartPieAlert(id)
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, cn.third),
|
||||
showInMore: false
|
||||
}
|
||||
// { title: t('analyzeTab.chartMenus.addToDashboard'), fn: () => {}, showInMore: true }
|
||||
];
|
||||
|
||||
return (
|
||||
<Row>
|
||||
{/* {feedData.feeds.map((feed) => (
|
||||
<Col key={feed.id} md="6">
|
||||
<ChartWrapper
|
||||
title={`${t('analyzeTab.charts.topLanguages')} (${feed.feed})`}
|
||||
menus={barchartMenus(cn.first, feed.id)}
|
||||
>
|
||||
<ECharts
|
||||
xLabel={barCountriesData.labels}
|
||||
loading={barCountriesData.loading}
|
||||
options={barCountriesData.data[feed.feed]}
|
||||
message={
|
||||
barCountriesData.error && barCountriesData.error[feed.feed]
|
||||
}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
))} */}
|
||||
{feedData.feeds.map((feed) => (
|
||||
<Col key={feed.id} md="6">
|
||||
<ChartWrapper
|
||||
title={`${t('analyzeTab.charts.topLanguages')} (${feed.feed})`}
|
||||
menus={barchartMenus(cn.second, feed.id)}
|
||||
>
|
||||
<ECharts
|
||||
xLabel={barLanguagesData.labels}
|
||||
loading={barLanguagesData.loading}
|
||||
options={barLanguagesData.data[feed.feed]}
|
||||
message={
|
||||
barLanguagesData.error && barLanguagesData.error[feed.feed]
|
||||
}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
))}
|
||||
{feedData.feeds.map((feed) => (
|
||||
<Col key={feed.id} md="6">
|
||||
<ChartWrapper
|
||||
title={`${t('analyzeTab.charts.gender')} (${feed.feed})`}
|
||||
menus={piechartMenus(feed.id)}
|
||||
>
|
||||
<ECharts
|
||||
loading={genderData.loading}
|
||||
options={genderData.data[feed.feed]}
|
||||
message={genderData.error && genderData.error[feed.feed]}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
const cn = {
|
||||
first: 'Top Countries',
|
||||
second: 'Top Languages',
|
||||
third: 'Gender'
|
||||
};
|
||||
|
||||
Demographics.propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
chartData: PropTypes.object,
|
||||
actions: PropTypes.object,
|
||||
id: PropTypes.string,
|
||||
feedData: PropTypes.object,
|
||||
analyze: PropTypes.object
|
||||
};
|
||||
|
||||
const applyDecorators = compose(
|
||||
reduxConnect('analyze', ['appState', 'analyze']),
|
||||
translate(['tabsContent'], { wait: true })
|
||||
);
|
||||
|
||||
export default applyDecorators(React.memo(Demographics));
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React, {
|
||||
useState,
|
||||
useCallback,
|
||||
Fragment,
|
||||
useEffect,
|
||||
useMemo
|
||||
} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { translate } from 'react-i18next';
|
||||
import { compose } from 'redux';
|
||||
import { Table } from '../../../../../common/Table/Table';
|
||||
import { getInfluencersAPI } from '../../../../../../api/analytics/createAnalytics';
|
||||
import { reduxActions } from '../../../../../../redux/utils/connect';
|
||||
import {
|
||||
getQueryParams,
|
||||
removeHttpsUrl,
|
||||
capOnlyFirstLetter,
|
||||
getValidHttpUrl
|
||||
} from '../../../../../../common/helper';
|
||||
import i18n from '../../../../../../i18n';
|
||||
|
||||
function Influencers(props) {
|
||||
const [dataSource, setDataSource] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [filter] = useState(filtersNames[1].id);
|
||||
const { t, actions, id, feedData } = props;
|
||||
|
||||
useEffect(() => {
|
||||
if (!id || !dataSource) {
|
||||
return;
|
||||
}
|
||||
getInfluencers(); //called from table
|
||||
}, [filter]);
|
||||
|
||||
const getDetailsColumns = (id) => {
|
||||
return id === filtersNames[0].id ? sourceDetails : authorDetails;
|
||||
};
|
||||
|
||||
const authorDetails = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.rank'),
|
||||
accessor: 'source_hashcode',
|
||||
Cell: (row) => (
|
||||
<div style={{ textAlign: 'center' }}>{row.index + 1}</div>
|
||||
),
|
||||
minWidth: 52
|
||||
},
|
||||
{
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.influencers'),
|
||||
accessor: 'influence',
|
||||
Cell: (row) =>
|
||||
getValidHttpUrl(row.value) ? (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="nofollow noopener"
|
||||
href={getValidHttpUrl(row.value)}
|
||||
>
|
||||
{row.original && row.original.author_name}
|
||||
</a>
|
||||
) : (
|
||||
removeHttpsUrl(row.value)
|
||||
),
|
||||
minWidth: 130
|
||||
},
|
||||
{
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.sourceType'),
|
||||
accessor: 'source_type',
|
||||
Cell: (row) => capOnlyFirstLetter(row.value),
|
||||
minWidth: 102
|
||||
}
|
||||
],
|
||||
[i18n.language]
|
||||
);
|
||||
|
||||
const sourceDetails = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.rank'),
|
||||
accessor: 'source_hashcode',
|
||||
Cell: (row) => (
|
||||
<div style={{ textAlign: 'center' }}>{row.index + 1}</div>
|
||||
),
|
||||
minWidth: 52
|
||||
},
|
||||
{
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.influencers'),
|
||||
accessor: 'influence',
|
||||
Cell: (row) =>
|
||||
getValidHttpUrl(row.value) ? (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="nofollow noopener"
|
||||
href={getValidHttpUrl(row.value)}
|
||||
>
|
||||
{removeHttpsUrl(row.value)}
|
||||
</a>
|
||||
) : (
|
||||
removeHttpsUrl(row.value)
|
||||
),
|
||||
minWidth: 130
|
||||
},
|
||||
{
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.sourceType'),
|
||||
accessor: 'source_type',
|
||||
Cell: (row) => capOnlyFirstLetter(row.value),
|
||||
minWidth: 102
|
||||
}
|
||||
],
|
||||
[i18n.language]
|
||||
);
|
||||
|
||||
const sentimentColumns = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.total'),
|
||||
// accessor: d => d.nop.total
|
||||
accessor: 'totalSentiment',
|
||||
minWidth: 52,
|
||||
Cell: (row) => (
|
||||
<div style={{ textAlign: 'center' }}>{row.value || 0}</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.positive'),
|
||||
accessor: 'POSITIVE',
|
||||
minWidth: 78,
|
||||
Cell: (row) => (
|
||||
<div style={{ textAlign: 'center' }}>{row.value || 0}</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.neutral'),
|
||||
accessor: 'NEUTRAL',
|
||||
minWidth: 78,
|
||||
Cell: (row) => (
|
||||
<div style={{ textAlign: 'center' }}>{row.value || 0}</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.negative'),
|
||||
accessor: 'NEGATIVE',
|
||||
minWidth: 78,
|
||||
Cell: (row) => (
|
||||
<div style={{ textAlign: 'center' }}>{row.value || 0}</div>
|
||||
)
|
||||
}
|
||||
],
|
||||
[i18n.language]
|
||||
);
|
||||
|
||||
const reachColumns = useMemo(
|
||||
() => [
|
||||
/* {
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.reach'),
|
||||
accessor: 'reach',
|
||||
minWidth: 65,
|
||||
Cell: (row) => <div style={{ textAlign: 'center' }}>{row.value || 0}</div>
|
||||
}, */
|
||||
{
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.engagement'),
|
||||
accessor: 'engagement',
|
||||
minWidth: 105,
|
||||
Cell: (row) => (
|
||||
<div style={{ textAlign: 'center' }}>{row.value || 0}</div>
|
||||
)
|
||||
}
|
||||
/* {
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.engagementPerMention'),
|
||||
accessor: 'engagement_per_mention',
|
||||
Cell: (row) => <div style={{ textAlign: 'center' }}>{row.value || 0}</div>
|
||||
} */
|
||||
],
|
||||
[i18n.language]
|
||||
);
|
||||
|
||||
const columnsList = useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.details'),
|
||||
headerClassName: 'text-center',
|
||||
columns: getDetailsColumns(filter)
|
||||
},
|
||||
{
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.sentiments'),
|
||||
headerClassName: 'text-center',
|
||||
columns: sentimentColumns
|
||||
},
|
||||
{
|
||||
Header: i18n.t('tabsContent:analyzeTab.influencerCols.reach'),
|
||||
headerClassName: 'text-center',
|
||||
columns: reachColumns
|
||||
}
|
||||
],
|
||||
[filter, i18n.language]
|
||||
);
|
||||
|
||||
const getInfluencers = useCallback(
|
||||
(page = 0, pageSize = 10) => {
|
||||
setLoading(true);
|
||||
const filterParams = getQueryParams({ page, pageSize });
|
||||
getInfluencersAPI(id, filter, filterParams).then((res) => {
|
||||
// if (false) {
|
||||
if (res.error || res.data === null || !res.data.data) {
|
||||
setLoading(false);
|
||||
return actions.addAlert({
|
||||
type: 'error',
|
||||
transKey: 'somethingWrong'
|
||||
});
|
||||
}
|
||||
|
||||
const tableData = {};
|
||||
res.data.data.forEach((v) => {
|
||||
tableData[v.name] = v.data;
|
||||
});
|
||||
setDataSource(tableData);
|
||||
setLoading(false);
|
||||
});
|
||||
},
|
||||
[id, filter]
|
||||
);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{/* <ButtonGroup size="sm" className="mb-3 d-block text-right">
|
||||
{filtersNames.map((item) => (
|
||||
<Button
|
||||
outline
|
||||
key={item.id}
|
||||
title={item.name}
|
||||
color="secondary"
|
||||
onClick={function () {
|
||||
setFilter(item.id)
|
||||
}}
|
||||
active={filter === item.id}
|
||||
>
|
||||
{item.name}
|
||||
</Button>
|
||||
))}
|
||||
</ButtonGroup> */}
|
||||
{feedData.feeds.map((feed) => {
|
||||
let tableData = dataSource;
|
||||
if (!tableData || !tableData[feed.feed]) {
|
||||
tableData = { [feed.feed]: [] };
|
||||
// uncomment for pagination
|
||||
// tableData[feed.feed] = { data: [], totalCount: 0, limit: 0, page: 0 }
|
||||
}
|
||||
|
||||
const { totalCount = 0, limit = 0, page = 0 } = tableData[feed.feed];
|
||||
return (
|
||||
<Table
|
||||
key={feed.id}
|
||||
t={t}
|
||||
cardTitle={`${t('analyzeTab.charts.topInfluencers')} (${
|
||||
feed.feed
|
||||
})`}
|
||||
columns={columnsList}
|
||||
data={tableData[feed.feed]}
|
||||
totalCount={totalCount}
|
||||
showTotalCount
|
||||
limit={limit}
|
||||
page={page}
|
||||
isLoading={loading}
|
||||
onFetchData={getInfluencers}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
const filtersNames = [
|
||||
{ name: 'Source', id: 0 },
|
||||
{ name: 'Author', id: 1 }
|
||||
];
|
||||
|
||||
Influencers.propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
feedData: PropTypes.object,
|
||||
id: PropTypes.string,
|
||||
actions: PropTypes.object
|
||||
};
|
||||
|
||||
const applyDecorators = compose(
|
||||
translate(['tabsContent'], { wait: true }),
|
||||
reduxActions()
|
||||
);
|
||||
|
||||
export default applyDecorators(Influencers);
|
||||
+722
@@ -0,0 +1,722 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Col, Row } from 'reactstrap';
|
||||
import ECharts from '../../../../../common/charts/ECharts';
|
||||
import ChartWrapper from '../ChartWrapper';
|
||||
import {
|
||||
getBarOptions,
|
||||
getPieOptions
|
||||
} from '../../../../../common/charts/ChartsOptions';
|
||||
import { IoIosAdd, IoIosRefresh, IoIosCheckmark } from 'react-icons/io';
|
||||
import reduxConnect from '../../../../../../redux/utils/connect';
|
||||
import translate from 'react-i18next/dist/commonjs/translate';
|
||||
import { compose } from 'redux';
|
||||
import {
|
||||
getEngagementsAPI,
|
||||
getEngagementsTimeAPI,
|
||||
getOverviewBarAPI,
|
||||
getOverviewPieAPI
|
||||
} from '../../../../../../api/analytics/createAnalytics';
|
||||
import useIsMounted from '../../../../../common/hooks/useIsMounted';
|
||||
|
||||
function Performance(props) {
|
||||
const { actions, analyze, feedData, id, t } = props;
|
||||
const isMounted = useIsMounted();
|
||||
const [barData, setBarData] = useState({
|
||||
data: [],
|
||||
error: undefined,
|
||||
loading: true,
|
||||
vertical: false
|
||||
});
|
||||
const [engBarData, setEngBarData] = useState({
|
||||
data: [],
|
||||
error: undefined,
|
||||
loading: true,
|
||||
vertical: false
|
||||
});
|
||||
const [potentialBarData, setPotentialBarData] = useState({
|
||||
data: [],
|
||||
error: undefined,
|
||||
loading: true,
|
||||
vertical: false
|
||||
});
|
||||
const [sentimentBar, setSentimentBar] = useState({
|
||||
data: [],
|
||||
error: undefined,
|
||||
loading: true
|
||||
});
|
||||
const [pieMentions, setpieMentions] = useState({
|
||||
data: [],
|
||||
error: undefined,
|
||||
loading: true
|
||||
});
|
||||
const [pieEng, setpieEng] = useState({
|
||||
data: [],
|
||||
error: undefined,
|
||||
loading: true
|
||||
});
|
||||
/* const [pieReach, setpieReach] = useState({
|
||||
data: [],
|
||||
error: undefined,
|
||||
loading: true
|
||||
}); */
|
||||
|
||||
useEffect(() => {
|
||||
// pass filter
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
getBarChart();
|
||||
getEngBarChart();
|
||||
// getPotentialChart()
|
||||
getSentimentChart();
|
||||
getpieMentions();
|
||||
getpieEngg();
|
||||
// getpieReach()
|
||||
}, []);
|
||||
|
||||
function updateResult(foo, id) {
|
||||
switch (id) {
|
||||
case cn.first:
|
||||
getBarChart();
|
||||
return;
|
||||
case cn.second:
|
||||
getEngBarChart();
|
||||
return;
|
||||
case cn.third:
|
||||
// getPotentialChart() // Uncomment when API has data
|
||||
return;
|
||||
case cn.fourth:
|
||||
getSentimentChart();
|
||||
return;
|
||||
case cn.fifth:
|
||||
getpieMentions();
|
||||
return;
|
||||
case cn.sixth:
|
||||
getpieEngg();
|
||||
return;
|
||||
case cn.seventh:
|
||||
// getpieReach() // Uncomment when API has data
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (barData.data) {
|
||||
setBarData((prev) => ({
|
||||
...prev,
|
||||
data: {
|
||||
...prev.data,
|
||||
xAxis: prev.data.yAxis,
|
||||
yAxis: prev.data.xAxis
|
||||
}
|
||||
}));
|
||||
}
|
||||
}, [barData.vertical]);
|
||||
|
||||
useEffect(() => {
|
||||
if (engBarData.data) {
|
||||
setEngBarData((prev) => ({
|
||||
...prev,
|
||||
data: {
|
||||
...prev.data,
|
||||
xAxis: prev.data.yAxis,
|
||||
yAxis: prev.data.xAxis
|
||||
}
|
||||
}));
|
||||
}
|
||||
}, [engBarData.vertical]);
|
||||
|
||||
useEffect(() => {
|
||||
if (potentialBarData.data) {
|
||||
setPotentialBarData((prev) => ({
|
||||
...prev,
|
||||
data: {
|
||||
...prev.data,
|
||||
xAxis: prev.data.yAxis,
|
||||
yAxis: prev.data.xAxis
|
||||
}
|
||||
}));
|
||||
}
|
||||
}, [potentialBarData.vertical]);
|
||||
|
||||
function getBarChart() {
|
||||
setBarData((prev) => ({ ...prev, loading: true }));
|
||||
getOverviewBarAPI('none', id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// on error
|
||||
setBarData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
const { data } = res.data;
|
||||
const labels = Object.keys(data[0].data);
|
||||
|
||||
const datasets = data.map((item) => ({
|
||||
name: item.name,
|
||||
type: barData.vertical ? 'bar' : 'line',
|
||||
smooth: true,
|
||||
data: Object.values(item.data)
|
||||
}));
|
||||
|
||||
const barOptions = getBarOptions(datasets, labels);
|
||||
|
||||
setBarData({
|
||||
data: barOptions,
|
||||
error: false,
|
||||
loading: false,
|
||||
vertical: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getEngBarChart() {
|
||||
setEngBarData((prev) => ({ ...prev, loading: true }));
|
||||
getEngagementsTimeAPI(id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// on error
|
||||
setEngBarData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = res.data;
|
||||
const labels = Object.keys(data[0].data);
|
||||
const datasets = data.map((item) => ({
|
||||
name: item.name,
|
||||
type: barData.vertical ? 'bar' : 'line',
|
||||
smooth: true,
|
||||
data: Object.values(item.data)
|
||||
}));
|
||||
|
||||
const barOptions = getBarOptions(datasets, labels);
|
||||
|
||||
setEngBarData({
|
||||
data: barOptions,
|
||||
error: false,
|
||||
loading: false,
|
||||
vertical: false
|
||||
});
|
||||
});
|
||||
}
|
||||
/*
|
||||
function getPotentialChart() {
|
||||
setPotentialBarData((prev) => ({ ...prev, loading: true }));
|
||||
getOverviewBarAPI('none', id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// on error
|
||||
setPotentialBarData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
const { data } = res.data;
|
||||
const labels = Object.keys(data);
|
||||
|
||||
const datasets = {
|
||||
name: 'Potential reach over time',
|
||||
type: potentialBarData.vertical ? 'bar' : 'line',
|
||||
smooth: true,
|
||||
data: Object.values(data)
|
||||
};
|
||||
|
||||
const barOptions = getBarOptions(datasets, labels);
|
||||
|
||||
setPotentialBarData({
|
||||
data: barOptions,
|
||||
error: false,
|
||||
loading: false,
|
||||
vertical: false
|
||||
});
|
||||
});
|
||||
} */
|
||||
|
||||
function getSentimentChart() {
|
||||
setSentimentBar((prev) => ({ ...prev, loading: true }));
|
||||
getOverviewPieAPI('sentiment', id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// on error
|
||||
setSentimentBar((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
const { data } = res.data;
|
||||
const barOptions = {};
|
||||
Object.keys(data).forEach((feed) => {
|
||||
const labels = ['Results'];
|
||||
const datasets = ['POSITIVE', 'NEGATIVE', 'NEUTRAL'].map((item) => ({
|
||||
name: item,
|
||||
type: 'bar',
|
||||
data: [data[feed][item]]
|
||||
}));
|
||||
|
||||
barOptions[feed] = getBarOptions(datasets, labels);
|
||||
});
|
||||
|
||||
setSentimentBar({
|
||||
data: barOptions,
|
||||
error: false,
|
||||
loading: false,
|
||||
vertical: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getpieMentions() {
|
||||
setpieMentions((prev) => ({ ...prev, loading: true }));
|
||||
getOverviewPieAPI('none', id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// alert on error
|
||||
setpieMentions((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = res.data;
|
||||
const pieOptions = getPieOptions(
|
||||
Object.entries(data).map((v) => ({ name: v[0], value: v[1] }))
|
||||
);
|
||||
|
||||
setpieMentions({
|
||||
data: pieOptions,
|
||||
error: false,
|
||||
loading: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getpieEngg() {
|
||||
setpieEng((prev) => ({ ...prev, loading: true }));
|
||||
getEngagementsAPI(id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// alert on error
|
||||
setpieEng((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
// condition for other filter than 0
|
||||
const { data } = res.data;
|
||||
const pieOptions = getPieOptions(
|
||||
Object.entries(data).map((v) => ({ name: v[0], value: v[1] }))
|
||||
);
|
||||
|
||||
setpieEng({
|
||||
data: pieOptions,
|
||||
error: false,
|
||||
loading: false
|
||||
});
|
||||
});
|
||||
}
|
||||
/*
|
||||
function getpieReach() {
|
||||
setpieReach((prev) => ({ ...prev, loading: true }));
|
||||
getOverviewPieAPI('none', id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// alert on error
|
||||
setpieReach((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = res.data;
|
||||
const pieOptions = getPieOptions(
|
||||
Object.entries(data).map((v) => ({ name: v[0], value: v[1] }))
|
||||
);
|
||||
|
||||
setpieReach({
|
||||
data: pieOptions,
|
||||
error: false,
|
||||
loading: false
|
||||
});
|
||||
});
|
||||
} */
|
||||
|
||||
function changeVertical(chart) {
|
||||
switch (chart) {
|
||||
case cn.first:
|
||||
setBarData((prev) => ({ ...prev, vertical: !prev.vertical }));
|
||||
return;
|
||||
case cn.second:
|
||||
setEngBarData((prev) => ({ ...prev, vertical: !prev.vertical }));
|
||||
return;
|
||||
case cn.third:
|
||||
setPotentialBarData((prev) => ({ ...prev, vertical: !prev.vertical }));
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const hideChart1Alert = analyze.alertCharts.find((v) => v.name === cn.first);
|
||||
const hideChart2Alert = analyze.alertCharts.find((v) => v.name === cn.second);
|
||||
// const hideChart3Alert = analyze.alertCharts.find((v) => v.name === cn.third);
|
||||
const hideChart4Alert = (id) =>
|
||||
analyze.alertCharts.find((v) => v.name === cn.fourth && v.id === id);
|
||||
const hideChart5Alert = analyze.alertCharts.find((v) => v.name === cn.fifth);
|
||||
const hideChart6Alert = analyze.alertCharts.find((v) => v.name === cn.sixth);
|
||||
/* const hideChart7Alert = analyze.alertCharts.find(
|
||||
(v) => v.name === cn.seventh
|
||||
); */
|
||||
|
||||
const barchart1Menus = [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: cn.first, id: 'none' }),
|
||||
showInMore: false,
|
||||
hide: hideChart1Alert
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChart1Alert
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, cn.first),
|
||||
showInMore: false
|
||||
},
|
||||
/* {
|
||||
title: t('analyzeTab.chartMenus.addToDashboard'),
|
||||
fn: () => {},
|
||||
showInMore: true
|
||||
}, */
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.toggleHV'),
|
||||
fn: () => changeVertical(cn.first),
|
||||
showInMore: true
|
||||
}
|
||||
];
|
||||
|
||||
const barchart2Menus = [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: cn.second, id: 'none' }),
|
||||
showInMore: false,
|
||||
hide: hideChart2Alert
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChart2Alert
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, cn.second),
|
||||
showInMore: false
|
||||
},
|
||||
/* {
|
||||
title: t('analyzeTab.chartMenus.addToDashboard'),
|
||||
fn: () => {},
|
||||
showInMore: true
|
||||
}, */
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.toggleHV'),
|
||||
fn: () => changeVertical(cn.second),
|
||||
showInMore: true
|
||||
}
|
||||
];
|
||||
/*
|
||||
const barchart3Menus = [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: cn.third, id: 'none' }),
|
||||
showInMore: false,
|
||||
hide: hideChart3Alert
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChart3Alert
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, cn.third),
|
||||
showInMore: false
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.addToDashboard'),
|
||||
fn: () => {},
|
||||
showInMore: true
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.toggleHV'),
|
||||
fn: () => changeVertical(cn.third),
|
||||
showInMore: true
|
||||
}
|
||||
];
|
||||
*/
|
||||
function barchart4Menus(id) {
|
||||
return [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: cn.fourth, id }),
|
||||
showInMore: false,
|
||||
hide: hideChart4Alert(id)
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChart4Alert(id)
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, cn.fourth, id),
|
||||
showInMore: false
|
||||
}
|
||||
/* {
|
||||
title: t('analyzeTab.chartMenus.addToDashboard'),
|
||||
fn: () => {},
|
||||
showInMore: true
|
||||
} */
|
||||
];
|
||||
}
|
||||
|
||||
const pieChart1 = [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: cn.fifth, id: 'none' }),
|
||||
showInMore: false,
|
||||
hide: hideChart5Alert
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChart5Alert
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, cn.fifth),
|
||||
showInMore: false
|
||||
}
|
||||
// { title: t('analyzeTab.chartMenus.addToDashboard'), fn: () => {}, showInMore: true }
|
||||
];
|
||||
|
||||
const pieChart2 = [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: cn.sixth, id: 'none' }),
|
||||
showInMore: false,
|
||||
hide: hideChart6Alert
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChart6Alert
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, cn.sixth),
|
||||
showInMore: false
|
||||
}
|
||||
// { title: t('analyzeTab.chartMenus.addToDashboard'), fn: () => {}, showInMore: true }
|
||||
];
|
||||
/*
|
||||
const pieChart3 = [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: cn.seventh, id: 'none' }),
|
||||
showInMore: false,
|
||||
hide: hideChart7Alert
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChart7Alert
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, cn.seventh),
|
||||
showInMore: false
|
||||
}
|
||||
// { title: t('analyzeTab.chartMenus.addToDashboard'), fn: () => {}, showInMore: true }
|
||||
];
|
||||
*/
|
||||
return (
|
||||
<Row>
|
||||
<Col md="8">
|
||||
<ChartWrapper
|
||||
title={t('analyzeTab.charts.mentionsOverTime')}
|
||||
menus={barchart1Menus}
|
||||
>
|
||||
<ECharts
|
||||
xLabel={barData.labels}
|
||||
loading={barData.loading}
|
||||
options={barData.data}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
<Col md="4">
|
||||
<ChartWrapper title={t('analyzeTab.charts.mentions')} menus={pieChart1}>
|
||||
<ECharts
|
||||
xLabel={pieMentions.labels}
|
||||
loading={pieMentions.loading}
|
||||
options={pieMentions.data}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
<Col md="8">
|
||||
<ChartWrapper
|
||||
title={t('analyzeTab.charts.engagementOverTime')}
|
||||
menus={barchart2Menus}
|
||||
>
|
||||
<ECharts
|
||||
xLabel={engBarData.labels}
|
||||
loading={engBarData.loading}
|
||||
options={engBarData.data}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
<Col md="4">
|
||||
<ChartWrapper
|
||||
title={t('analyzeTab.charts.engagement')}
|
||||
menus={pieChart2}
|
||||
>
|
||||
<ECharts
|
||||
xLabel={pieEng.labels}
|
||||
loading={pieEng.loading}
|
||||
options={pieEng.data}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
{/* <Col md="8">
|
||||
<ChartWrapper title={t('analyzeTab.charts.potentialReachOverTime')} menus={barchart3Menus}>
|
||||
<ECharts
|
||||
xLabel={potentialBarData.labels}
|
||||
loading={potentialBarData.loading}
|
||||
options={potentialBarData.data}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
<Col md="4">
|
||||
<ChartWrapper title={t('analyzeTab.charts.potentialReach')} menus={pieChart3}>
|
||||
<ECharts
|
||||
xLabel={pieReach.labels}
|
||||
loading={pieReach.loading}
|
||||
options={pieReach.data}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col> */}
|
||||
{feedData.feeds.map((feed) => (
|
||||
<Col md="12" key={feed.id}>
|
||||
<ChartWrapper
|
||||
title={`${t('analyzeTab.charts.proportionofSentiment')} (${
|
||||
feed.feed
|
||||
})`}
|
||||
menus={barchart4Menus(feed.id)}
|
||||
>
|
||||
<ECharts
|
||||
xLabel={sentimentBar.labels}
|
||||
loading={sentimentBar.loading}
|
||||
options={sentimentBar.data[feed.feed]}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
const cn = {
|
||||
first: 'Mentions over time',
|
||||
second: 'Engagement over time',
|
||||
third: 'Potential reach over time',
|
||||
fourth: 'Proportion of sentiment',
|
||||
fifth: 'Mentions',
|
||||
sixth: 'Engagement',
|
||||
seventh: 'Potential Reach'
|
||||
};
|
||||
|
||||
Performance.propTypes = {
|
||||
chartData: PropTypes.object,
|
||||
actions: PropTypes.object,
|
||||
feedData: PropTypes.object,
|
||||
id: PropTypes.string,
|
||||
analyze: PropTypes.object,
|
||||
t: PropTypes.func
|
||||
};
|
||||
|
||||
const applyDecorators = compose(
|
||||
reduxConnect('analyze', ['appState', 'analyze']),
|
||||
translate(['tabsContent'], { wait: true })
|
||||
);
|
||||
|
||||
export default applyDecorators(React.memo(Performance));
|
||||
+403
@@ -0,0 +1,403 @@
|
||||
import React, { Fragment, useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, ButtonGroup, Col, Row } from 'reactstrap';
|
||||
import ECharts from '../../../../../common/charts/ECharts';
|
||||
import ChartWrapper from '../ChartWrapper';
|
||||
import {
|
||||
getBarOptions,
|
||||
getPieOptions
|
||||
} from '../../../../../common/charts/ChartsOptions';
|
||||
import { IoIosAdd, IoIosRefresh, IoIosCheckmark } from 'react-icons/io';
|
||||
import reduxConnect from '../../../../../../redux/utils/connect';
|
||||
import translate from 'react-i18next/dist/commonjs/translate';
|
||||
import { compose } from 'redux';
|
||||
import {
|
||||
getOverviewBarAPI,
|
||||
getOverviewPieAPI
|
||||
} from '../../../../../../api/analytics/createAnalytics';
|
||||
import useIsMounted from '../../../../../common/hooks/useIsMounted';
|
||||
|
||||
const initialBar = {
|
||||
data: [],
|
||||
error: undefined,
|
||||
loading: true,
|
||||
vertical: false
|
||||
};
|
||||
|
||||
const initialPie = { data: [], error: undefined, loading: true };
|
||||
|
||||
function ResultsTab(props) {
|
||||
const { actions, analyze, feedData, id, t } = props;
|
||||
const isMounted = useIsMounted();
|
||||
const [barData, setBarData] = useState(initialBar);
|
||||
const [barTimeData, setBarTimeData] = useState(initialBar);
|
||||
const [pieData, setPieData] = useState(initialPie);
|
||||
const [pieTimeData, setPieTimeData] = useState(initialPie);
|
||||
const [filter, setFilter] = useState(filtersNames[0].id);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
if (filter === filtersNames[0].id) {
|
||||
getBarChart();
|
||||
getPieChart();
|
||||
} else {
|
||||
getBarChartFeeds();
|
||||
getPieChartFeeds();
|
||||
}
|
||||
}, [filter]);
|
||||
|
||||
useEffect(() => {
|
||||
if (barData.data) {
|
||||
setBarData((prev) => ({
|
||||
...prev,
|
||||
data: {
|
||||
...prev.data,
|
||||
xAxis: prev.data.yAxis,
|
||||
yAxis: prev.data.xAxis
|
||||
}
|
||||
}));
|
||||
}
|
||||
}, [barData.vertical]);
|
||||
|
||||
function updateResult(foo, id) {
|
||||
switch (id) {
|
||||
case cn.first:
|
||||
filter === filtersNames[0].id ? getBarChart() : getBarChartFeeds();
|
||||
return;
|
||||
case cn.second:
|
||||
filter === filtersNames[0].id ? getPieChart() : getPieChartFeeds();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function getBarChart() {
|
||||
setBarData((prev) => ({ ...prev, loading: true }));
|
||||
getOverviewBarAPI(filter, id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// on error
|
||||
setBarData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
const { data } = res.data;
|
||||
const labels = data[0] ? Object.keys(data[0].data) : [];
|
||||
const datasets = data.map((item) => ({
|
||||
name: item.name,
|
||||
type: barData.vertical ? 'bar' : 'line',
|
||||
smooth: true,
|
||||
data: Object.values(item.data)
|
||||
}));
|
||||
|
||||
const barOptions = getBarOptions(datasets, labels);
|
||||
|
||||
setBarData({
|
||||
data: barOptions,
|
||||
error: false,
|
||||
loading: false,
|
||||
vertical: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getBarChartFeeds() {
|
||||
setBarTimeData((prev) => ({ ...prev, loading: true }));
|
||||
getOverviewBarAPI(filter, id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (res.error || !res.data.data) {
|
||||
// on error
|
||||
setBarTimeData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
const { data } = res.data;
|
||||
const barOptions = {};
|
||||
const errors = {};
|
||||
|
||||
data.map((feed) => {
|
||||
const { name, data } = feed;
|
||||
|
||||
if (!data || (Array.isArray(data) && data.length < 1)) {
|
||||
errors[name] = t('analyzeTab.noData');
|
||||
return;
|
||||
}
|
||||
|
||||
const labels = Object.keys(data[0].data).sort();
|
||||
const datasets = data.map((item) => ({
|
||||
name: item.name,
|
||||
type: barTimeData.vertical ? 'bar' : 'line',
|
||||
smooth: true,
|
||||
data: labels.map((v) => item.data[v])
|
||||
}));
|
||||
|
||||
barOptions[name] = getBarOptions(datasets, labels);
|
||||
});
|
||||
|
||||
setBarTimeData({
|
||||
data: barOptions,
|
||||
error: errors,
|
||||
loading: false,
|
||||
vertical: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getPieChart() {
|
||||
setPieData((prev) => ({ ...prev, loading: true }));
|
||||
getOverviewPieAPI(filter, id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// alert on error
|
||||
setPieData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = res.data;
|
||||
const pieOptions = getPieOptions(
|
||||
Object.entries(data).map((v) => ({ name: v[0], value: v[1] }))
|
||||
);
|
||||
|
||||
setPieData({
|
||||
data: pieOptions,
|
||||
error: false,
|
||||
loading: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getPieChartFeeds() {
|
||||
setPieTimeData((prev) => ({ ...prev, loading: true }));
|
||||
getOverviewPieAPI(filter, id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// alert on error
|
||||
setPieTimeData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = res.data;
|
||||
const pieOptions = {};
|
||||
const errors = {};
|
||||
|
||||
Object.entries(data).forEach((feed) => {
|
||||
const [name, value] = feed;
|
||||
|
||||
if (!value || (Array.isArray(value) && value.length < 1)) {
|
||||
errors[name] = t('analyzeTab.noData');
|
||||
}
|
||||
|
||||
pieOptions[name] = getPieOptions(
|
||||
Object.entries(value).map((v) => ({
|
||||
name: v[0],
|
||||
value: v[1]
|
||||
}))
|
||||
);
|
||||
});
|
||||
|
||||
setPieTimeData({
|
||||
data: pieOptions,
|
||||
error: errors,
|
||||
loading: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function changeVertical() {
|
||||
setBarData((prev) => ({ ...prev, vertical: !prev.vertical }));
|
||||
}
|
||||
|
||||
const hideChart1Alert = (id) =>
|
||||
analyze.alertCharts.find((v) => v.name === cn.first && v.id === id);
|
||||
const hideChart2Alert = (id) =>
|
||||
analyze.alertCharts.find((v) => v.name === cn.second && v.id === id);
|
||||
|
||||
const barchartMenus = (id) => [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: cn.first, id }),
|
||||
showInMore: false,
|
||||
hide: hideChart1Alert(id)
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChart1Alert(id)
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, cn.first),
|
||||
showInMore: false
|
||||
},
|
||||
/* {
|
||||
title: t('analyzeTab.chartMenus.addToDashboard'),
|
||||
fn: () => {},
|
||||
showInMore: true
|
||||
}, */
|
||||
{
|
||||
title: 'Toggle Horizontal/Vertical',
|
||||
fn: changeVertical,
|
||||
showInMore: true
|
||||
}
|
||||
];
|
||||
const piechartMenus = (id) => [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: cn.second, id }),
|
||||
showInMore: false,
|
||||
hide: hideChart2Alert(id)
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChart2Alert(id)
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, cn.second),
|
||||
showInMore: false
|
||||
}
|
||||
// { title: t('analyzeTab.chartMenus.addToDashboard'), fn: () => {}, showInMore: true }
|
||||
];
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="mask-line overflow-auto white-space-nowrap pl-3 mb-3">
|
||||
<ButtonGroup size="sm">
|
||||
{filtersNames.map((item) => (
|
||||
<Button
|
||||
outline
|
||||
key={item.id}
|
||||
title={item.name}
|
||||
color="secondary"
|
||||
onClick={function () {
|
||||
setFilter(item.id);
|
||||
}}
|
||||
active={filter === item.id}
|
||||
>
|
||||
{t(`analyzeTab.overviewCharts.${item.transKey}`)}
|
||||
</Button>
|
||||
))}
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
{filter === filtersNames[0].id ? ( // feeds in single graph
|
||||
<Row>
|
||||
<Col md="8">
|
||||
<ChartWrapper
|
||||
title={t('analyzeTab.charts.mentionsOverTime')}
|
||||
menus={barchartMenus('none')}
|
||||
>
|
||||
<ECharts
|
||||
xLabel={barData.labels}
|
||||
loading={barData.loading}
|
||||
options={barData.data}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
<Col md="4">
|
||||
<ChartWrapper
|
||||
title={t('analyzeTab.charts.mentions')}
|
||||
menus={piechartMenus('none')}
|
||||
>
|
||||
<ECharts loading={pieData.loading} options={pieData.data} />
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
</Row>
|
||||
) : (
|
||||
feedData.feeds.map((feed) => (
|
||||
<Row key={feed.id}>
|
||||
<Col md="8">
|
||||
<ChartWrapper
|
||||
title={`${t('analyzeTab.charts.mentionsOverTime')} (${
|
||||
feed.feed
|
||||
})`}
|
||||
menus={barchartMenus(feed.id)}
|
||||
>
|
||||
<ECharts
|
||||
xLabel={barTimeData.labels}
|
||||
loading={barTimeData.loading}
|
||||
options={barTimeData.data && barTimeData.data[feed.feed]}
|
||||
message={barTimeData.error && barTimeData.error[feed.feed]}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
<Col md="4">
|
||||
<ChartWrapper
|
||||
title={`${t('analyzeTab.charts.mentions')} (${feed.feed})`}
|
||||
menus={piechartMenus(feed.id)}
|
||||
>
|
||||
<ECharts
|
||||
loading={pieTimeData.loading}
|
||||
options={pieTimeData.data[feed.feed]}
|
||||
message={pieTimeData.error && pieTimeData.error[feed.feed]}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
</Row>
|
||||
))
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
const cn = {
|
||||
first: 'Mentions Over Time',
|
||||
second: 'Share of Mentions'
|
||||
};
|
||||
|
||||
const filtersNames = [
|
||||
{ name: 'None', transKey: 'none', id: 'none' },
|
||||
{ name: 'Media Types', transKey: 'mediaTypes', id: 'media' },
|
||||
{ name: 'Sentiments', transKey: 'sentiments', id: 'sentiment' },
|
||||
// { name: 'Countries', transKey:'countries', id: 'country' },
|
||||
{ name: 'Languages', transKey: 'languages', id: 'language' }
|
||||
];
|
||||
|
||||
ResultsTab.propTypes = {
|
||||
actions: PropTypes.object,
|
||||
id: PropTypes.string,
|
||||
t: PropTypes.func,
|
||||
feedData: PropTypes.object,
|
||||
analyze: PropTypes.object
|
||||
};
|
||||
|
||||
const applyDecorators = compose(
|
||||
reduxConnect('analyze', ['appState', 'analyze']),
|
||||
translate(['tabsContent'], { wait: true })
|
||||
);
|
||||
|
||||
export default applyDecorators(React.memo(ResultsTab));
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Col, Row } from 'reactstrap';
|
||||
import ECharts from '../../../../../common/charts/ECharts';
|
||||
import ChartWrapper from '../ChartWrapper';
|
||||
import {
|
||||
getBarOptions,
|
||||
getPieOptions
|
||||
} from '../../../../../common/charts/ChartsOptions';
|
||||
import { IoIosAdd, IoIosRefresh, IoIosCheckmark } from 'react-icons/io';
|
||||
import reduxConnect from '../../../../../../redux/utils/connect';
|
||||
import translate from 'react-i18next/dist/commonjs/translate';
|
||||
import { compose } from 'redux';
|
||||
import {
|
||||
getOverviewBarAPI,
|
||||
getOverviewPieAPI
|
||||
} from '../../../../../../api/analytics/createAnalytics';
|
||||
import useIsMounted from '../../../../../common/hooks/useIsMounted';
|
||||
|
||||
const initialBar = {
|
||||
data: [],
|
||||
error: undefined,
|
||||
loading: true,
|
||||
vertical: false
|
||||
};
|
||||
|
||||
const initialPie = { data: [], error: undefined, loading: true };
|
||||
|
||||
function Sentiment(props) {
|
||||
const { actions, analyze, feedData, id, t } = props;
|
||||
const isMounted = useIsMounted();
|
||||
const [barData, setBarData] = useState(initialBar);
|
||||
const [pieData, setPieData] = useState(initialPie);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
getBarChart();
|
||||
getPieChart();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (barData.data) {
|
||||
setBarData((prev) => ({
|
||||
...prev,
|
||||
data: {
|
||||
...prev.data,
|
||||
xAxis: prev.data.yAxis,
|
||||
yAxis: prev.data.xAxis
|
||||
}
|
||||
}));
|
||||
}
|
||||
}, [barData.vertical]);
|
||||
|
||||
function updateResult(foo, id) {
|
||||
switch (id) {
|
||||
case cn.first:
|
||||
getBarChart();
|
||||
return;
|
||||
case cn.second:
|
||||
getPieChart();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function getBarChart() {
|
||||
setBarData((prev) => ({ ...prev, loading: true }));
|
||||
getOverviewBarAPI('sentiment', id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// on error
|
||||
setBarData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
const { data } = res.data;
|
||||
const barOptions = {};
|
||||
data.forEach((feed) => {
|
||||
const { name, data } = feed;
|
||||
const labels = Object.keys(data[0].data).sort();
|
||||
const datasets = data.map((item) => ({
|
||||
name: item.name,
|
||||
type: barData.vertical ? 'bar' : 'line',
|
||||
smooth: true,
|
||||
data: labels.map((v) => item.data[v])
|
||||
}));
|
||||
|
||||
barOptions[name] = getBarOptions(datasets, labels);
|
||||
});
|
||||
|
||||
setBarData({
|
||||
data: barOptions,
|
||||
error: false,
|
||||
loading: false,
|
||||
vertical: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getPieChart() {
|
||||
setPieData((prev) => ({ ...prev, loading: true }));
|
||||
getOverviewPieAPI('sentiment', id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// alert on error
|
||||
setPieData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = res.data;
|
||||
const pieOptions = {};
|
||||
Object.entries(data).forEach((feed) => {
|
||||
const [name, value] = feed;
|
||||
pieOptions[name] = getPieOptions(
|
||||
Object.entries(value).map((v) => ({
|
||||
name: v[0],
|
||||
value: v[1]
|
||||
}))
|
||||
);
|
||||
});
|
||||
|
||||
setPieData({
|
||||
data: pieOptions,
|
||||
error: false,
|
||||
loading: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function changeVertical() {
|
||||
setBarData((prev) => ({ ...prev, vertical: !prev.vertical }));
|
||||
}
|
||||
|
||||
const hideChart1Alert = (id) =>
|
||||
analyze.alertCharts.find((v) => v.name === cn.first && v.id === id);
|
||||
const hideChart2Alert = (id) =>
|
||||
analyze.alertCharts.find((v) => v.name === cn.second && v.id === id);
|
||||
|
||||
const barchartMenus = (id) => [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: cn.first, id }),
|
||||
showInMore: false,
|
||||
hide: hideChart1Alert(id)
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChart1Alert(id)
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, cn.first),
|
||||
showInMore: false
|
||||
},
|
||||
/* {
|
||||
title: t('analyzeTab.chartMenus.addToDashboard'),
|
||||
fn: () => {},
|
||||
showInMore: true
|
||||
}, */
|
||||
{
|
||||
title: 'Toggle Horizontal/Vertical',
|
||||
fn: changeVertical,
|
||||
showInMore: true
|
||||
}
|
||||
];
|
||||
const piechartMenus = (id) => [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: cn.second, id }),
|
||||
showInMore: false,
|
||||
hide: hideChart2Alert(id)
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChart2Alert(id)
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, cn.second),
|
||||
showInMore: false
|
||||
}
|
||||
// { title: t('analyzeTab.chartMenus.addToDashboard'), fn: () => {}, showInMore: true }
|
||||
];
|
||||
|
||||
return feedData.feeds.map((feed) => (
|
||||
<Row key={feed.id}>
|
||||
<Col md="8">
|
||||
<ChartWrapper
|
||||
title={`${t('analyzeTab.charts.sentimentOverTime')} (${feed.feed})`}
|
||||
menus={barchartMenus(feed.id)}
|
||||
>
|
||||
<ECharts
|
||||
xLabel={barData.labels}
|
||||
loading={barData.loading}
|
||||
options={barData.data[feed.feed]}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
<Col md="4">
|
||||
<ChartWrapper
|
||||
title={`${t('analyzeTab.charts.shareofSentiment')} (${feed.feed})`}
|
||||
menus={piechartMenus(feed.id)}
|
||||
>
|
||||
<ECharts
|
||||
loading={pieData.loading}
|
||||
options={pieData.data[feed.feed]}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
</Row>
|
||||
));
|
||||
}
|
||||
|
||||
const cn = {
|
||||
first: 'Sentiment Over Time',
|
||||
second: 'Share of Sentiment'
|
||||
};
|
||||
|
||||
Sentiment.propTypes = {
|
||||
actions: PropTypes.object,
|
||||
feedData: PropTypes.object,
|
||||
analyze: PropTypes.object,
|
||||
t: PropTypes.func
|
||||
};
|
||||
|
||||
const applyDecorators = compose(
|
||||
reduxConnect('analyze', ['appState', 'analyze']),
|
||||
translate(['tabsContent'], { wait: true })
|
||||
);
|
||||
|
||||
export default applyDecorators(React.memo(Sentiment));
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Col, Row } from 'reactstrap';
|
||||
import ECharts from '../../../../../common/charts/ECharts';
|
||||
import 'echarts-wordcloud';
|
||||
import { capitalize } from 'lodash';
|
||||
import ChartWrapper from '../ChartWrapper';
|
||||
import {
|
||||
getBarOptions,
|
||||
PieToolbox,
|
||||
WordCloudOptions
|
||||
} from '../../../../../common/charts/ChartsOptions';
|
||||
import { IoIosAdd, IoIosRefresh, IoIosCheckmark } from 'react-icons/io';
|
||||
import reduxConnect from '../../../../../../redux/utils/connect';
|
||||
import translate from 'react-i18next/dist/commonjs/translate';
|
||||
import { compose } from 'redux';
|
||||
import {
|
||||
getThemesCloudAPI,
|
||||
getThemesTimeAPI
|
||||
} from '../../../../../../api/analytics/createAnalytics';
|
||||
import useIsMounted from '../../../../../common/hooks/useIsMounted';
|
||||
import { capFirstLetter } from '../../../../../../common/helper';
|
||||
|
||||
const initialBar = {
|
||||
data: [],
|
||||
error: undefined,
|
||||
loading: true,
|
||||
vertical: false
|
||||
};
|
||||
|
||||
const initialPie = { data: [], error: undefined, loading: true };
|
||||
|
||||
function Themes(props) {
|
||||
const { actions, analyze, feedData, id, t } = props;
|
||||
const isMounted = useIsMounted();
|
||||
const [barData, setBarData] = useState(initialBar);
|
||||
const [wordData, setWordData] = useState(initialPie);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
getBarChart();
|
||||
getWordCloud();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (barData.data) {
|
||||
setBarData((prev) => ({
|
||||
...prev,
|
||||
data: {
|
||||
...prev.data,
|
||||
xAxis: prev.data.yAxis,
|
||||
yAxis: prev.data.xAxis
|
||||
}
|
||||
}));
|
||||
}
|
||||
}, [barData.vertical]);
|
||||
|
||||
function updateResult(foo, id) {
|
||||
switch (id) {
|
||||
case cn.first:
|
||||
getBarChart();
|
||||
return;
|
||||
case cn.second:
|
||||
getWordCloud();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function getBarChart() {
|
||||
setBarData((prev) => ({ ...prev, loading: true }));
|
||||
getThemesTimeAPI(id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// on error
|
||||
setBarData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
const { data } = res.data;
|
||||
let labels = null;
|
||||
const barOptions = {};
|
||||
const errors = {};
|
||||
data.forEach((feedData) => {
|
||||
const { name, data } = feedData;
|
||||
const datasets = data.map((item) => ({
|
||||
name: capitalize(item.name),
|
||||
type: barData.vertical ? 'bar' : 'line',
|
||||
smooth: true,
|
||||
data: Object.values(item.data)
|
||||
}));
|
||||
|
||||
if (!labels && data && data[0] && data[0].data) {
|
||||
labels = Object.keys(data[0].data);
|
||||
}
|
||||
|
||||
barOptions[name] = getBarOptions(datasets, labels);
|
||||
|
||||
if (!datasets || (Array.isArray(datasets) && datasets.length < 1)) {
|
||||
errors[name] = t('analyzeTab.noData');
|
||||
}
|
||||
});
|
||||
|
||||
setBarData({
|
||||
data: barOptions,
|
||||
error: errors,
|
||||
loading: false,
|
||||
vertical: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getWordCloud() {
|
||||
setWordData((prev) => ({ ...prev, loading: true }));
|
||||
getThemesCloudAPI(id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// alert on error
|
||||
setWordData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = res.data;
|
||||
const cloudOptions = {};
|
||||
const errors = {};
|
||||
data.forEach((feed) => {
|
||||
const { name, data } = feed;
|
||||
if (!data || (Array.isArray(data) && data.length < 1)) {
|
||||
errors[name] = t('analyzeTab.noData');
|
||||
}
|
||||
|
||||
cloudOptions[name] = {
|
||||
tooltip: {
|
||||
show: true
|
||||
},
|
||||
toolbox: PieToolbox,
|
||||
series: [
|
||||
{
|
||||
...WordCloudOptions,
|
||||
data: Object.entries(data).map((v) => ({
|
||||
name: capFirstLetter(v[0]),
|
||||
value: v[1]
|
||||
}))
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
setWordData({
|
||||
data: cloudOptions,
|
||||
error: false,
|
||||
loading: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function changeVertical() {
|
||||
setBarData((prev) => ({ ...prev, vertical: !prev.vertical }));
|
||||
}
|
||||
|
||||
const hideChart1Alert = (id) =>
|
||||
analyze.alertCharts.find((v) => v.name === cn.first && v.id === id);
|
||||
const hideChart2Alert = (id) =>
|
||||
analyze.alertCharts.find((v) => v.name === cn.second && v.id === id);
|
||||
|
||||
const barchartMenus = (id) => [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: cn.first, id }),
|
||||
showInMore: false,
|
||||
hide: hideChart1Alert(id)
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChart1Alert(id)
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, cn.first),
|
||||
showInMore: false
|
||||
},
|
||||
/* {
|
||||
title: t('analyzeTab.chartMenus.addToDashboard'),
|
||||
fn: () => {},
|
||||
showInMore: true
|
||||
}, */
|
||||
{
|
||||
title: 'Toggle Horizontal/Vertical',
|
||||
fn: changeVertical,
|
||||
showInMore: true
|
||||
}
|
||||
];
|
||||
const wordCloudMenus = (id) => [
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addToAlert'),
|
||||
icon: IoIosAdd,
|
||||
size: 24,
|
||||
fn: () => actions.addAlertChart({ name: cn.second, id }),
|
||||
showInMore: false,
|
||||
hide: hideChart2Alert(id)
|
||||
},
|
||||
{
|
||||
title: '', // t('analyzeTab.chartMenus.addedToAlerts'),
|
||||
icon: IoIosCheckmark,
|
||||
size: 24,
|
||||
showInMore: false,
|
||||
hide: !hideChart2Alert(id)
|
||||
},
|
||||
{
|
||||
title: t('analyzeTab.chartMenus.refresh'),
|
||||
icon: IoIosRefresh,
|
||||
fn: () => updateResult(null, cn.second),
|
||||
showInMore: false
|
||||
}
|
||||
// { title: t('analyzeTab.chartMenus.addToDashboard'), fn: () => {}, showInMore: true }
|
||||
];
|
||||
|
||||
return feedData.feeds.map((feed) => (
|
||||
<Row key={feed.id}>
|
||||
<Col md="8">
|
||||
<ChartWrapper
|
||||
title={`${t('analyzeTab.charts.themesOverTime')} (${feed.feed})`}
|
||||
menus={barchartMenus(feed.id)}
|
||||
>
|
||||
<ECharts
|
||||
xLabel={barData.labels}
|
||||
loading={barData.loading}
|
||||
options={barData.data[feed.feed]}
|
||||
message={barData.error && barData.error[feed.feed]}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
<Col md="4">
|
||||
<ChartWrapper
|
||||
title={`${t('analyzeTab.charts.topThemes')} (${feed.feed})`}
|
||||
menus={wordCloudMenus(feed.id)}
|
||||
>
|
||||
<ECharts
|
||||
loading={wordData.loading}
|
||||
options={wordData.data[feed.feed]}
|
||||
message={barData.error && barData.error[feed.feed]}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
</Row>
|
||||
));
|
||||
}
|
||||
|
||||
const cn = {
|
||||
first: 'Themes over time',
|
||||
second: 'Top Themes'
|
||||
};
|
||||
|
||||
Themes.propTypes = {
|
||||
chartData: PropTypes.object,
|
||||
actions: PropTypes.object,
|
||||
feedData: PropTypes.object,
|
||||
t: PropTypes.func,
|
||||
analyze: PropTypes.object
|
||||
};
|
||||
|
||||
const applyDecorators = compose(
|
||||
reduxConnect('analyze', ['appState', 'analyze']),
|
||||
translate(['tabsContent'], { wait: true })
|
||||
);
|
||||
|
||||
export default applyDecorators(React.memo(Themes));
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
import React, { useEffect, useRef, useState, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Row, Col, ButtonGroup, Button } from 'reactstrap';
|
||||
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import L from 'leaflet';
|
||||
import 'leaflet-dvf/dist/leaflet-dvf';
|
||||
// keep above 3 in sequence
|
||||
import ChartWrapper from '../ChartWrapper';
|
||||
import { getWorldMapAPI } from '../../../../../../api/analytics/createAnalytics';
|
||||
import useIsMounted from '../../../../../common/hooks/useIsMounted';
|
||||
import { translate } from 'react-i18next';
|
||||
|
||||
const initialPie = {
|
||||
data: [],
|
||||
error: undefined,
|
||||
loading: true,
|
||||
selected: undefined
|
||||
};
|
||||
|
||||
function WorldMap(props) {
|
||||
const { id, t } = props;
|
||||
const mapRef = useRef();
|
||||
const isMounted = useIsMounted();
|
||||
const [pieData, setPieData] = useState(initialPie);
|
||||
const [markers, setMarkers] = useState([]);
|
||||
|
||||
const feedNames = (pieData.data && Object.keys(pieData.data)) || [];
|
||||
|
||||
useEffect(() => {
|
||||
mapRef.current = L.map('leaflet-map', {
|
||||
center: [0, 0],
|
||||
zoom: 2,
|
||||
layers: [
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
noWrap: true,
|
||||
attribution:
|
||||
'© <a target="_blank" noreferrer noopener href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
mapRef.current.whenReady(getMapSentiments);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const { data, selected, error } = pieData;
|
||||
const selectedData = data[feedNames[selected]];
|
||||
const hasErr = error && error[feedNames[selected]];
|
||||
clearMap();
|
||||
|
||||
if (selectedData && !hasErr) {
|
||||
// loop to add marker
|
||||
const markersList = [];
|
||||
selectedData.forEach((data) => {
|
||||
const [lat, lng] = getLatLong(data.LatLng);
|
||||
if (!lat || !lng) {
|
||||
return;
|
||||
}
|
||||
|
||||
let pieChartMarker = new L.PieChartMarker(new L.LatLng(lat, lng), {
|
||||
...options,
|
||||
data: {
|
||||
positive: data.POSITIVE,
|
||||
negative: data.NEGATIVE,
|
||||
neutral: data.NEUTRAL
|
||||
}
|
||||
});
|
||||
pieChartMarker.addTo(mapRef.current);
|
||||
markersList.push(pieChartMarker);
|
||||
});
|
||||
// eslint-disable-next-line new-cap
|
||||
const group = new L.featureGroup(markersList);
|
||||
mapRef.current.fitBounds(group.getBounds());
|
||||
setMarkers(markersList);
|
||||
}
|
||||
}, [pieData.data, pieData.selected]);
|
||||
|
||||
function getLatLong(str) {
|
||||
const [lat, lng] = str.split(', ');
|
||||
return [lat && parseFloat(lat), lng && parseFloat(lng)];
|
||||
}
|
||||
|
||||
function clearMap() {
|
||||
if (mapRef.current) {
|
||||
markers.forEach((v) => {
|
||||
mapRef.current.removeLayer(v);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getMapSentiments() {
|
||||
setPieData((prev) => ({ ...prev, loading: true }));
|
||||
getWorldMapAPI(id).then((res) => {
|
||||
if (!isMounted.current) {
|
||||
return false;
|
||||
}
|
||||
if (res.error || !res.data.data) {
|
||||
// alert on error
|
||||
setPieData((prev) => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: res.errorMessage
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = res.data;
|
||||
const dataValues = {};
|
||||
const errors = {};
|
||||
|
||||
data.map((feed) => {
|
||||
const { name, data } = feed;
|
||||
if (!data || (Array.isArray(data) && data.length < 1)) {
|
||||
errors[name] = t('analyzeTab.noData');
|
||||
}
|
||||
dataValues[name] = data;
|
||||
});
|
||||
|
||||
setPieData({
|
||||
data: dataValues,
|
||||
error: errors,
|
||||
loading: false,
|
||||
selected: 0
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const style = {
|
||||
height: 'max(300px, calc(100vh - 200px))'
|
||||
};
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Col md="12">
|
||||
<ChartWrapper title="Distribution by Sentiments">
|
||||
<Fragment>
|
||||
<ButtonGroup size="sm" className="d-block mb-2 text-right">
|
||||
{feedNames.map((name, i) => (
|
||||
<Button
|
||||
outline
|
||||
key={name}
|
||||
title={name}
|
||||
color="secondary"
|
||||
onClick={function () {
|
||||
setPieData((prev) => ({
|
||||
...prev,
|
||||
selected: i
|
||||
}));
|
||||
}}
|
||||
active={pieData.selected === i}
|
||||
>
|
||||
{name}
|
||||
</Button>
|
||||
))}
|
||||
</ButtonGroup>
|
||||
<div className="position-relative">
|
||||
<div id="leaflet-map" style={style} />
|
||||
{pieData.error && pieData.error[feedNames[pieData.selected]] ? (
|
||||
<div className="no-data" style={{ zIndex: 1000 }}>
|
||||
{pieData.error[feedNames[pieData.selected]]}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</Fragment>
|
||||
</ChartWrapper>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
const options = {
|
||||
stroke: false,
|
||||
fillOpacity: 0.7,
|
||||
radius: 20,
|
||||
gradient: false,
|
||||
chartOptions: {
|
||||
positive: {
|
||||
fillColor: '#00FF00',
|
||||
displayText: function (value) {
|
||||
return value.toFixed(0);
|
||||
}
|
||||
},
|
||||
negative: {
|
||||
fillColor: '#FF0000',
|
||||
displayText: function (value) {
|
||||
return value.toFixed(0);
|
||||
}
|
||||
},
|
||||
neutral: {
|
||||
fillColor: '#000000',
|
||||
displayText: function (value) {
|
||||
return value.toFixed(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Other L.Path style options
|
||||
};
|
||||
|
||||
WorldMap.propTypes = {
|
||||
actions: PropTypes.object,
|
||||
feedData: PropTypes.object,
|
||||
id: PropTypes.string,
|
||||
t: PropTypes.func.isRequired,
|
||||
analyze: PropTypes.object
|
||||
};
|
||||
|
||||
export default translate(['tabsContent'], { wait: true })(WorldMap);
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import Results from './Results'
|
||||
import Performance from './Performance'
|
||||
import Influencers from './Influencers'
|
||||
import Sentiment from './Sentiment'
|
||||
import Themes from './Themes'
|
||||
import Demographics from './Demographics'
|
||||
import WorldMap from './WorldMap'
|
||||
|
||||
export {
|
||||
Results,
|
||||
Performance,
|
||||
Influencers,
|
||||
Sentiment,
|
||||
Themes,
|
||||
Demographics,
|
||||
WorldMap
|
||||
}
|
||||
Reference in New Issue
Block a user