UX: Convert sentiment analysis overview to horizontal bars (#1216)
The sentiment analysis report page initially showcases sentiments via category/tag by doughnut visualizations. However, this isn't an optimal view for quickly scanning and comparing each result. This PR updates the overview to include a table visualization with horizontal bars to represent sentiment analysis instead of doughnuts. Doughnut visualizations are still maintained however when accessing the sentiment data in the drill down for individual entries. This approach is an intermediary step, as we will eventually add whole clustering and sizing visualization instead of a table. As such, no relevant tests are added in this PR.
This commit is contained in:
parent
76a48786d9
commit
9dfae3d472
|
@ -23,6 +23,7 @@ import closeOnClickOutside from "discourse/modifiers/close-on-click-outside";
|
|||
import { i18n } from "discourse-i18n";
|
||||
import DTooltip from "float-kit/components/d-tooltip";
|
||||
import DoughnutChart from "discourse/plugins/discourse-ai/discourse/components/doughnut-chart";
|
||||
import AiSentimentHorizontalBar from "../components/ai-sentiment-horizontal-bar";
|
||||
|
||||
export default class AdminReportSentimentAnalysis extends Component {
|
||||
@service router;
|
||||
|
@ -82,6 +83,15 @@ export default class AdminReportSentimentAnalysis extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
get groupingType() {
|
||||
const dataSample = this.args.model.data[0];
|
||||
const localePrefix =
|
||||
"discourse_ai.sentiments.sentiment_analysis.group_types";
|
||||
return dataSample.category_name
|
||||
? i18n(`${localePrefix}.category`)
|
||||
: i18n(`${localePrefix}.tag`);
|
||||
}
|
||||
|
||||
get colors() {
|
||||
return ["#2ecc71", "#95a5a6", "#e74c3c"];
|
||||
}
|
||||
|
@ -107,7 +117,17 @@ export default class AdminReportSentimentAnalysis extends Component {
|
|||
this.calculateNeutralScore(data),
|
||||
data.negative_count,
|
||||
],
|
||||
score_map: {
|
||||
positive: data.positive_count,
|
||||
neutral: this.calculateNeutralScore(data),
|
||||
negative: data.negative_count,
|
||||
},
|
||||
total_score: data.total_count,
|
||||
widths: {
|
||||
positive: (data.positive_count / data.total_count) * 100,
|
||||
neutral: (this.calculateNeutralScore(data) / data.total_count) * 100,
|
||||
negative: (data.negative_count / data.total_count) * 100,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -302,29 +322,56 @@ export default class AdminReportSentimentAnalysis extends Component {
|
|||
|
||||
{{#unless this.showingSelectedChart}}
|
||||
<div class="admin-report-sentiment-analysis">
|
||||
{{#each this.transformedData as |data|}}
|
||||
<div
|
||||
class="admin-report-sentiment-analysis__chart-wrapper"
|
||||
role="button"
|
||||
{{on "click" (fn this.showDetails data)}}
|
||||
{{closeOnClickOutside
|
||||
(fn (mut this.selectedChart) null)
|
||||
(hash
|
||||
targetSelector=".admin-report-sentiment-analysis-details"
|
||||
secondaryTargetSelector=".admin-report-sentiment-analysis"
|
||||
)
|
||||
}}
|
||||
>
|
||||
<DoughnutChart
|
||||
@labels={{@model.labels}}
|
||||
@colors={{this.colors}}
|
||||
@data={{data.scores}}
|
||||
@totalScore={{data.total_score}}
|
||||
@doughnutTitle={{data.title}}
|
||||
@displayLegend={{true}}
|
||||
/>
|
||||
</div>
|
||||
{{/each}}
|
||||
<table class="sentiment-analysis-table md-table">
|
||||
<thead>
|
||||
<th>{{this.groupingType}}</th>
|
||||
<th>{{i18n
|
||||
"discourse_ai.sentiments.sentiment_analysis.table.total_count"
|
||||
}}</th>
|
||||
<th>{{i18n
|
||||
"discourse_ai.sentiments.sentiment_analysis.table.sentiment"
|
||||
}}</th>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{{#each this.transformedData as |data|}}
|
||||
<tr
|
||||
class="sentiment-analysis-table__row"
|
||||
role="button"
|
||||
{{on "click" (fn this.showDetails data)}}
|
||||
{{closeOnClickOutside
|
||||
(fn (mut this.selectedChart) null)
|
||||
(hash
|
||||
targetSelector=".admin-report-sentiment-analysis-details"
|
||||
secondaryTargetSelector=".admin-report-sentiment-analysis"
|
||||
)
|
||||
}}
|
||||
>
|
||||
<td class="sentiment-analysis-table__title">{{data.title}}</td>
|
||||
<td
|
||||
class="sentiment-analysis-table__total-score"
|
||||
>{{data.total_score}}</td>
|
||||
<td class="sentiment-horizontal-bar">
|
||||
<AiSentimentHorizontalBar
|
||||
@type="positive"
|
||||
@score={{data.score_map.positive}}
|
||||
@width={{data.widths.positive}}
|
||||
/>
|
||||
<AiSentimentHorizontalBar
|
||||
@type="negative"
|
||||
@score={{data.score_map.negative}}
|
||||
@width={{data.widths.negative}}
|
||||
/>
|
||||
<AiSentimentHorizontalBar
|
||||
@type="neutral"
|
||||
@score={{data.score_map.neutral}}
|
||||
@width={{data.widths.neutral}}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import { concat } from "@ember/helper";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { gt } from "truth-helpers";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import DTooltip from "float-kit/components/d-tooltip";
|
||||
|
||||
const AiSentimentHorizontalBar = <template>
|
||||
{{#if (gt @score 0)}}
|
||||
<DTooltip
|
||||
class={{concat "sentiment-horizontal-bar__" @type}}
|
||||
style={{htmlSafe (concat "width: " @width "%")}}
|
||||
>
|
||||
<:trigger>
|
||||
<span class="sentiment-horizontal-bar__count">
|
||||
{{@score}}
|
||||
</span>
|
||||
</:trigger>
|
||||
<:content>
|
||||
{{i18n
|
||||
(concat
|
||||
"discourse_ai.sentiments.sentiment_analysis.filter_types." @type
|
||||
)
|
||||
}}:
|
||||
{{@score}}
|
||||
</:content>
|
||||
</DTooltip>
|
||||
{{/if}}
|
||||
</template>;
|
||||
|
||||
export default AiSentimentHorizontalBar;
|
|
@ -248,3 +248,54 @@
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sentiment-analysis-table {
|
||||
margin: 1rem;
|
||||
|
||||
&__total-score {
|
||||
font-weight: bold;
|
||||
font-size: var(--font-up-1);
|
||||
}
|
||||
|
||||
&__row {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.sentiment-horizontal-bar {
|
||||
display: flex;
|
||||
|
||||
&__count {
|
||||
font-weight: bold;
|
||||
font-size: var(--font-down-1);
|
||||
color: var(--secondary);
|
||||
}
|
||||
|
||||
&__positive,
|
||||
&__neutral,
|
||||
&__negative {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 0.75rem;
|
||||
border-left: 2px solid var(--secondary);
|
||||
border-right: 2px solid var(--secondary);
|
||||
}
|
||||
|
||||
&__positive {
|
||||
background: rgb(var(--d-sentiment-report-positive-rgb));
|
||||
border-top-left-radius: var(--d-border-radius);
|
||||
border-bottom-left-radius: var(--d-border-radius);
|
||||
}
|
||||
|
||||
&__negative {
|
||||
background: rgb(var(--d-sentiment-report-negative-rgb));
|
||||
}
|
||||
|
||||
&__neutral {
|
||||
background: rgb(var(--d-sentiment-report-neutral-rgb));
|
||||
border-top-right-radius: var(--d-border-radius);
|
||||
border-bottom-right-radius: var(--d-border-radius);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -700,6 +700,12 @@ en:
|
|||
positive: "Positive"
|
||||
neutral: "Neutral"
|
||||
negative: "Negative"
|
||||
group_types:
|
||||
category: "Category"
|
||||
tag: "Tag"
|
||||
table:
|
||||
sentiment: "Sentiment"
|
||||
total_count: "Total"
|
||||
|
||||
summarization:
|
||||
chat:
|
||||
|
|
Loading…
Reference in New Issue